Jump to content

Recommended Posts

Posted

I upgraded my PC recently and noticed that the throttle jitters, but not randomly as it's common with a conventional pot axis (why didn't saitek put a hall sensor in such an important axis :mad:), but rather it smoothly goes up and down at about 2-3 cycles per second. The amplitude is small enough to not to be a real problem when flying, even helicopters, as DCS seems to filter out lot of the input noise.

 

When looking at the axes in x52's control panel I can see the rotaries doing the same in unison, but not the slider axis nor the stick axes.

 

I'm not being very lucky trying to find any information about this behavior, what indicates it's not common at all.

 

Neither of my other controllers are doing something like that.

 

What I've tried so far: connect to different USB, open the throttle and spray all pots with contact cleaner (BTW no bare wires that I could see) and different driver versions.

I have thought about putting a capacitor in parallel to the 5V input, but neither the lights nor the MFD backlight show any pulsating change of intensity, so it may not be noise in the input power.

 

I may try it on my notebook and see what happens now that I think...

 

I'm posting with little hope of a fix, but just in case someone by chance has encounter the same problem. I can live with it anyway.

  • 4 years later...
Posted

I just found this thread, and I searched forever for a solution in case anyone needs it. While the numbers of X52 in the wild must be diminishing, this will also apply to other dodgy old Logitech/Saitek hardware. I had similar problems: both throttle rotaries and slider were noisy and indeed "jittery", as many have reported. I also noticed eventually that the throttle axis was also miss-behaving. (I could watch the virtual throttle of my aircraft shake slightly.)  This was the solution I found:

 

Its not the encoders or pots. 

 

Almost certainly anyway, its not them. I replaced them, with both the original 0.02$ parts and with Alps units, to no avail.  It worth pointing out this is only an issue with certain games (FS2020 is also affected), but, in the "USB controllers" part of Windows 10, everything seems mostly OK. Could be that there is something more fundamentally wrong with the electronics here.  As I said, I replaced the parts. I am not that experienced with electronics debugging but my bet is on there being an opamp IC somewhere that has gone to s**t (or started out that way more likely) and is adding noise to the circuit. If it indeed a hardware problem, and not DCS doing somthing unexpected. 

 

Solution that _worked_:

Joystick Gremlin. You also need to have vjoy installed.  I am not sure if vjoy is actively maintained but for now, as of early 2021, its fine to use.  Lastly, you will need the Joystick Gremlin macro linked to in this post.  Its copied below, in case that link goes cold. 

 

This is basically applies a configurable low-pass filter (default) or a Hamming window function (optional), depending on your needs. If none of that last sentence made any sense, just apply it with default values and you should be fine. 

 

Joystick Gremlin is a rather nice piece of software that maps your physical buttons, axies etc to a virtual controller hosted by vjoy (functionally excellent but not so nice, but you only need it to host a virtual controller so you never need to mess with it).  You need only map the functions you need (e.g, I mapped the rotaries, slider and main throttle axis only).  Gremlin takes the raw inputs, adds whatever filters you apply, and sends them to the vjoy device.  That vjoy device shows up as an additional controller in DCS, and you then map the axis to the vjoy device, instead of the physical device. 

 

Joystick Gremlin script to apply (sincere thanks + apologies to the author of this script, I have no idea who wrote it, nor how to find out, but it currently hosted on someone's Google Drive): 

 

from collections import deque
import threading
import time
import math

import gremlin
from gremlin.user_plugin import *


mode = ModeVariable("Mode", "The mode in which to use this mapping")

vjoy_axis = VirtualInputVariable(
        "Virtual output axis",
        "The vJoy axis to send the filtered output to.",
        [gremlin.common.InputType.JoystickAxis]
)

joy_axis = PhysicalInputVariable(
        "Physical input axis",
        "The physical input axis being filtered.",
        [gremlin.common.InputType.JoystickAxis]
)

sample_size = IntegerVariable(
        "Number of samples",
        "Number of samples to use to compute the average.",
        5,
        1,
        50
)

update_time_ms = IntegerVariable(
        "Update time (ms)",
        "Time between expected updates in milliseconds",
        250,
        10,
        5000
)

numvalues = IntegerVariable(
       "Resolution",
       "Number of values used to encode the axis",
       65536,
       2,
       65536	   
)

usehamming = BoolVariable(
      "Hamming",
	  "Use Hamming instead of moving average for filtering",
	  True)
 

# Global variables
g_samples = deque([], maxlen=sample_size.value)
g_last_value = 0.0
g_last_update = time.time()
g_thread = None
g_vjoy = gremlin.joystick_handling.VJoyProxy()

d_axis = joy_axis.create_decorator(mode.value)

g_prev_sample_size = 0
g_hamming_window = []

def update_thread():
    global g_samples
    update_time = update_time_ms.value / 1000.0

    while True:
        # Repeat last value
        if g_last_update + update_time < time.time():
            g_samples.append(apply_resolution(g_last_value))
            update_vjoy()

        # Ensure the thread terminates
        if (g_last_update + (update_time * g_samples.maxlen)) < time.time():
            return

        time.sleep(update_time)

def average():
    return sum(g_samples) / len(g_samples)

def hamming():
    global g_prev_sample_size, g_samples, g_hamming_window
    if sample_size.value != g_prev_sample_size:
        g_hamming_window = []
        total = 0    
        for i in range(sample_size.value):
            v = 0.54 - 0.46 * math.cos(2*math.pi*i/(sample_size.value-1))
            total = total + v
            g_hamming_window.append(v)
        for i in range(sample_size.value):
            g_hamming_window[i]=g_hamming_window[i]/total
        g_prev_sample_size = sample_size.value
    return sum([a*b for a,b in zip(g_samples,g_hamming_window)])
	
def median():
    n = len(g_samples)
    s = sorted(g_samples)
    return (sum(s[n//2-1:n//2+1])/2.0, s[n//2])[n % 2] if n else None
	
def apply_resolution(x):
    nvmin1 = numvalues.value-1
    v=-1+2*round(nvmin1*(x+1)/2)/nvmin1
    if v > 1.0:
        v = 1.0
    elif v < -1.0:
        v = -1.0
    return v

def update_vjoy():
    global g_vjoy
    if usehamming.value:
        v = hamming()
    else:
        v = median()
    g_vjoy[vjoy_axis.vjoy_id].axis(vjoy_axis.input_id).value = apply_resolution(v)


@d_axis.axis(joy_axis.input_id)
def axis_cb(event):
    global g_samples, g_last_value, g_last_update, g_thread

    g_last_value = event.value
    g_last_update = time.time()
    g_samples.append(event.value)
    update_vjoy()

    if g_thread is None or not g_thread.is_alive():
        g_thread = threading.Thread(target=update_thread)
        g_thread.start()

 

Posted
21 hours ago, milo1973 said:

I just found this thread, and I searched forever for a solution in case anyone needs it. While the numbers of X52 in the wild must be diminishing, this will also apply to other dodgy old Logitech/Saitek hardware. I had similar problems: both throttle rotaries and slider were noisy and indeed "jittery", as many have reported. I also noticed eventually that the throttle axis was also miss-behaving. (I could watch the virtual throttle of my aircraft shake slightly.)  This was the solution I found:

 

Its not the encoders or pots. 

 

Almost certainly anyway, its not them. I replaced them, with both the original 0.02$ parts and with Alps units, to no avail.  It worth pointing out this is only an issue with certain games (FS2020 is also affected), but, in the "USB controllers" part of Windows 10, everything seems mostly OK. Could be that there is something more fundamentally wrong with the electronics here.  As I said, I replaced the parts. I am not that experienced with electronics debugging but my bet is on there being an opamp IC somewhere that has gone to s**t (or started out that way more likely) and is adding noise to the circuit. If it indeed a hardware problem, and not DCS doing somthing unexpected. 

 

Solution that _worked_:

Joystick Gremlin. You also need to have vjoy installed.  I am not sure if vjoy is actively maintained but for now, as of early 2021, its fine to use.  Lastly, you will need the Joystick Gremlin macro linked to in this post.  Its copied below, in case that link goes cold. 

 

This is basically applies a configurable low-pass filter (default) or a Hamming window function (optional), depending on your needs. If none of that last sentence made any sense, just apply it with default values and you should be fine. 

 

Joystick Gremlin is a rather nice piece of software that maps your physical buttons, axies etc to a virtual controller hosted by vjoy (functionally excellent but not so nice, but you only need it to host a virtual controller so you never need to mess with it).  You need only map the functions you need (e.g, I mapped the rotaries, slider and main throttle axis only).  Gremlin takes the raw inputs, adds whatever filters you apply, and sends them to the vjoy device.  That vjoy device shows up as an additional controller in DCS, and you then map the axis to the vjoy device, instead of the physical device. 

 

Joystick Gremlin script to apply (sincere thanks + apologies to the author of this script, I have no idea who wrote it, nor how to find out, but it currently hosted on someone's Google Drive): 

 


from collections import deque
import threading
import time
import math

import gremlin
from gremlin.user_plugin import *


mode = ModeVariable("Mode", "The mode in which to use this mapping")

vjoy_axis = VirtualInputVariable(
        "Virtual output axis",
        "The vJoy axis to send the filtered output to.",
        [gremlin.common.InputType.JoystickAxis]
)

joy_axis = PhysicalInputVariable(
        "Physical input axis",
        "The physical input axis being filtered.",
        [gremlin.common.InputType.JoystickAxis]
)

sample_size = IntegerVariable(
        "Number of samples",
        "Number of samples to use to compute the average.",
        5,
        1,
        50
)

update_time_ms = IntegerVariable(
        "Update time (ms)",
        "Time between expected updates in milliseconds",
        250,
        10,
        5000
)

numvalues = IntegerVariable(
       "Resolution",
       "Number of values used to encode the axis",
       65536,
       2,
       65536	   
)

usehamming = BoolVariable(
      "Hamming",
	  "Use Hamming instead of moving average for filtering",
	  True)
 

# Global variables
g_samples = deque([], maxlen=sample_size.value)
g_last_value = 0.0
g_last_update = time.time()
g_thread = None
g_vjoy = gremlin.joystick_handling.VJoyProxy()

d_axis = joy_axis.create_decorator(mode.value)

g_prev_sample_size = 0
g_hamming_window = []

def update_thread():
    global g_samples
    update_time = update_time_ms.value / 1000.0

    while True:
        # Repeat last value
        if g_last_update + update_time < time.time():
            g_samples.append(apply_resolution(g_last_value))
            update_vjoy()

        # Ensure the thread terminates
        if (g_last_update + (update_time * g_samples.maxlen)) < time.time():
            return

        time.sleep(update_time)

def average():
    return sum(g_samples) / len(g_samples)

def hamming():
    global g_prev_sample_size, g_samples, g_hamming_window
    if sample_size.value != g_prev_sample_size:
        g_hamming_window = []
        total = 0    
        for i in range(sample_size.value):
            v = 0.54 - 0.46 * math.cos(2*math.pi*i/(sample_size.value-1))
            total = total + v
            g_hamming_window.append(v)
        for i in range(sample_size.value):
            g_hamming_window[i]=g_hamming_window[i]/total
        g_prev_sample_size = sample_size.value
    return sum([a*b for a,b in zip(g_samples,g_hamming_window)])
	
def median():
    n = len(g_samples)
    s = sorted(g_samples)
    return (sum(s[n//2-1:n//2+1])/2.0, s[n//2])[n % 2] if n else None
	
def apply_resolution(x):
    nvmin1 = numvalues.value-1
    v=-1+2*round(nvmin1*(x+1)/2)/nvmin1
    if v > 1.0:
        v = 1.0
    elif v < -1.0:
        v = -1.0
    return v

def update_vjoy():
    global g_vjoy
    if usehamming.value:
        v = hamming()
    else:
        v = median()
    g_vjoy[vjoy_axis.vjoy_id].axis(vjoy_axis.input_id).value = apply_resolution(v)


@d_axis.axis(joy_axis.input_id)
def axis_cb(event):
    global g_samples, g_last_value, g_last_update, g_thread

    g_last_value = event.value
    g_last_update = time.time()
    g_samples.append(event.value)
    update_vjoy()

    if g_thread is None or not g_thread.is_alive():
        g_thread = threading.Thread(target=update_thread)
        g_thread.start()

 

 

Hey there Milo,

 

I have just bought the x-52 two weeks ago and I am experiencing the jitter issue you mentioned with the throttle axis, "precision" slider and both rotaries. Can you elaborate more on how you setup these programs (vJoy and Joystick Gremlin)? Is there a possibility to get on discord to explain to me how can I fix these issues? I am a noob when it comes to dealing with scripts and programming. 

 

My discord is: psychoap87#9415 

 

Thanks in advance

  • Recently Browsing   0 members

    • No registered users viewing this page.
×
×
  • Create New...