legoEv3/evdev/ff.py

# encoding: utf-8

import ctypes
from evdev import ecodes


_u8  = ctypes.c_uint8
_u16 = ctypes.c_uint16
_u32 = ctypes.c_uint32
_s16 = ctypes.c_int16

class Replay(ctypes.Structure):
    '''
    Defines scheduling of the force-feedback effect
    @length: duration of the effect
    @delay: delay before effect should start playing
    '''

    _fields_ = [
        ('length', _u16),
        ('delay',  _u16),
    ]


class Trigger(ctypes.Structure):
    '''
    Defines what triggers the force-feedback effect
    @button: number of the button triggering the effect
    @interval: controls how soon the effect can be re-triggered
    '''

    _fields_ = [
        ('button', _u16),
        ('interval',  _u16),
    ]


class Envelope(ctypes.Structure):
    '''
    Generic force-feedback effect envelope
    @attack_length: duration of the attack (ms)
    @attack_level: level at the beginning of the attack
    @fade_length: duration of fade (ms)
    @fade_level: level at the end of fade

    The @attack_level and @fade_level are absolute values; when applying
    envelope force-feedback core will convert to positive/negative
    value based on polarity of the default level of the effect.
    Valid range for the attack and fade levels is 0x0000 - 0x7fff
    '''

    _fields_ = [
        ('attach_length', _u16),
        ('attack_level', _u16),
        ('fade_length', _u16),
        ('fade_level', _u16),
    ]


class Constant(ctypes.Structure):
    '''
    Defines parameters of a constant force-feedback effect
    @level: strength of the effect; may be negative
    @envelope: envelope data
    '''

    _fields_ = [
        ('level', _s16),
        ('ff_envelope', Envelope),
    ]


class Ramp(ctypes.Structure):
    '''
    Defines parameters of a ramp force-feedback effect
    @start_level: beginning strength of the effect; may be negative
    @end_level: final strength of the effect; may be negative
    @envelope: envelope data
    '''

    _fields_ = [
        ('start_level', _s16),
        ('end_level', _s16),
        ('ff_envelope', Envelope),
    ]


class Condition(ctypes.Structure):
    '''
    Defines a spring or friction force-feedback effect
    @right_saturation: maximum level when joystick moved all way to the right
    @left_saturation: same for the left side
    @right_coeff: controls how fast the force grows when the joystick moves to the right
    @left_coeff: same for the left side
    @deadband: size of the dead zone, where no force is produced
    @center: position of the dead zone
    '''

    _fields_ = [
        ('right_saturation', _u16),
        ('left_saturation', _u16),
        ('right_coeff', _s16),
        ('left_foeff', _s16),
        ('deadband', _u16),
        ('center', _s16),
    ]


class Periodic(ctypes.Structure):
    '''
    Defines parameters of a periodic force-feedback effect
    @waveform: kind of the effect (wave)
    @period: period of the wave (ms)
    @magnitude: peak value
    @offset: mean value of the wave (roughly)
    @phase: 'horizontal' shift
    @envelope: envelope data
    @custom_len: number of samples (FF_CUSTOM only)
    @custom_data: buffer of samples (FF_CUSTOM only)
    '''

    _fields_ = [
        ('waveform', _u16),
        ('period', _u16),
        ('magnitude', _s16),
        ('offset', _s16),
        ('phase', _u16),
        ('envelope', Envelope),
        ('custom_len', _u32),
        ('custom_data', ctypes.POINTER(_s16)),
    ]


class Rumble(ctypes.Structure):
    '''
    Defines parameters of a periodic force-feedback effect
    @strong_magnitude: magnitude of the heavy motor
    @weak_magnitude: magnitude of the light one

    Some rumble pads have two motors of different weight. Strong_magnitude
    represents the magnitude of the vibration generated by the heavy one.
    '''

    _fields_ = [
        ('strong_magnitude', _u16),
        ('weak_magnitude', _u16),
    ]


class EffectType(ctypes.Union):
    _fields_ = [
        ('ff_constant_effect', Constant),
        ('ff_ramp_effect', Ramp),
        ('ff_periodic_effect', Periodic),
        ('ff_condition_effect', Condition * 2),  # one for each axis
        ('ff_rumble_effect', Rumble),
    ]


class Effect(ctypes.Structure):
    _fields_ = [
        ('type', _u16),
        ('id', _s16),
        ('direction', _u16),
        ('ff_trigger', Trigger),
        ('ff_replay', Replay),
        ('u', EffectType)
    ]

# ff_types = {
#     ecodes.FF_CONSTANT,
#     ecodes.FF_PERIODIC,
#     ecodes.FF_RAMP,
#     ecodes.FF_SPRING,
#     ecodes.FF_FRICTION,
#     ecodes.FF_DAMPER,
#     ecodes.FF_RUMBLE,
#     ecodes.FF_INERTIA,
#     ecodes.FF_CUSTOM,
# }