Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
13 changes: 13 additions & 0 deletions scripts/autokey.py
Original file line number Diff line number Diff line change
Expand Up @@ -187,6 +187,19 @@ def line_starts_with(x: str) -> bool:
print(intf.version())
continue

elif line_equals(':humanizer'):
# Print humanizer level
print(intf.get_humanizer_level())
continue

elif line_starts_with(':humanizer'):
# Set humanizer level
try:
intf.set_humanizer_level(float(line[10:]))
except ValueError:
print('Invalid level?')
continue

elif line_starts_with(':'):
# Unknown command?
print('Unknown command?')
Expand Down
14 changes: 14 additions & 0 deletions scripts/superkey/interface.py
Original file line number Diff line number Diff line change
Expand Up @@ -170,6 +170,13 @@ def get_buzzer_frequency(self) -> int:
self.__send_packet(MessageID.REQUEST_GET_BUZZER_FREQUENCY)
return self.__check_reply('<H')[0]

def get_humanizer_level(self) -> float:
"""
Sends the `REQUEST_GET_HUMANIZER_LEVEL` command. Returns the current humanizer level as a fraction.
"""
self.__send_packet(MessageID.REQUEST_GET_HUMANIZER_LEVEL)
return self.__check_reply('<f')[0]

def get_invert_paddles(self) -> bool:
"""
Sends the `REQUEST_GET_INVERT_PADDLES` command. Returns whether or not the paddles are inverted.
Expand Down Expand Up @@ -289,6 +296,13 @@ def set_buzzer_frequency(self, frequency: int):
self.__send_packet(MessageID.REQUEST_SET_BUZZER_FREQUENCY, struct.pack('<H', frequency))
self.__check_reply_empty()

def set_humanizer_level(self, level: float):
"""
Sends the `REQUEST_SET_HUMANIZER_LEVEL` command. Sets the humanizer level as a fraction.
"""
self.__send_packet(MessageID.REQUEST_SET_HUMANIZER_LEVEL, struct.pack('<f', level))
self.__check_reply_empty()

def set_invert_paddles(self, inverted: bool):
"""
Sends the `REQUEST_SET_INVERT_PADDLES` command. Sets whether the paddles are inverted.
Expand Down
2 changes: 2 additions & 0 deletions scripts/superkey/types.py
Original file line number Diff line number Diff line change
Expand Up @@ -109,6 +109,7 @@
'REQUEST_AUTOKEY_QUICK_MSG',
'REQUEST_GET_BUZZER_ENABLED',
'REQUEST_GET_BUZZER_FREQUENCY',
'REQUEST_GET_HUMANIZER_LEVEL',
'REQUEST_GET_INVERT_PADDLES',
'REQUEST_GET_IO_POLARITY',
'REQUEST_GET_IO_STATE',
Expand All @@ -126,6 +127,7 @@
'REQUEST_RESTORE_DEFAULT_CONFIG',
'REQUEST_SET_BUZZER_ENABLED',
'REQUEST_SET_BUZZER_FREQUENCY',
'REQUEST_SET_HUMANIZER_LEVEL',
'REQUEST_SET_INVERT_PADDLES',
'REQUEST_SET_IO_POLARITY',
'REQUEST_SET_IO_TYPE',
Expand Down
11 changes: 9 additions & 2 deletions scripts/wordcopy.py
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@
DEFAULT_WPM = 20.
DEFAULT_MINLEN = 2
DEFAULT_MAXLEN = 8
DEFAULT_HUMANIZE = 0.

# ----------------------------------------------------- PROCEDURES -----------------------------------------------------

Expand All @@ -38,6 +39,7 @@ def _parse_args():
parser.add_argument('--wpm', type=float, default=DEFAULT_WPM, help='Words per minute.')
parser.add_argument('--minlen', type=int, default=DEFAULT_MINLEN, help='Minimum word length.')
parser.add_argument('--maxlen', type=int, default=DEFAULT_MAXLEN, help='Maximum word length.')
parser.add_argument('--humanizer', type=float, default=DEFAULT_HUMANIZE, help='Humanizer fraction.')
return parser.parse_args()

def _main(port: str,
Expand All @@ -47,7 +49,8 @@ def _main(port: str,
delay: float,
wpm: float,
minlen: int,
maxlen: int):
maxlen: int,
humanizer: float):
"""
Runs the trainer.
"""
Expand All @@ -62,10 +65,12 @@ def _main(port: str,
# Get initial settings
initial_wpm = intf.get_wpm()
initial_trainer_mode = intf.get_trainer_mode()
initial_humanizer_level = intf.get_humanizer_level()

# Override settings
intf.set_wpm(wpm)
intf.set_trainer_mode(True)
intf.set_humanizer_level(humanizer)

# Run as many times as commanded
for _ in range(count):
Expand All @@ -89,6 +94,7 @@ def _main(port: str,
# Restore initial settings
intf.set_wpm(initial_wpm)
intf.set_trainer_mode(initial_trainer_mode)
intf.set_humanizer_level(initial_humanizer_level)


# -------------------------------------------------- IMPORT PROCEDURE --------------------------------------------------
Expand All @@ -106,4 +112,5 @@ def _main(port: str,
delay = args.delay,
wpm = args.wpm,
minlen = args.minlen,
maxlen = args.maxlen)
maxlen = args.maxlen,
humanizer = args.humanizer)
44 changes: 44 additions & 0 deletions src/main/application/intf_port.c
Original file line number Diff line number Diff line change
Expand Up @@ -140,6 +140,12 @@ static void process_message_request_get_buzzer_enabled( intf_header_t const * he
*/
static void process_message_request_get_buzzer_frequency( intf_header_t const * header, void const * payload );

/**
* @fn process_message_request_get_humanizer_level( intf_header_t const *, void const * )
* @brief Processes the specified interface message with the `INTF_MESSAGE_REQUEST_GET_HUMANIZER_LEVEL` message ID.
*/
static void process_message_request_get_humanizer_level( intf_header_t const * header, void const * payload );

/**
* @fn process_message_request_get_invert_paddles( intf_header_t const *, void const * )
* @brief Processes the specified interface message with the `INTF_MESSAGE_REQUEST_GET_INVERT_PADDLES` message ID.
Expand Down Expand Up @@ -242,6 +248,12 @@ static void process_message_request_set_buzzer_enabled( intf_header_t const * he
*/
static void process_message_request_set_buzzer_frequency( intf_header_t const * header, void const * payload );

/**
* @fn process_message_request_set_humanizer_level( intf_header_t const *, void const * )
* @brief Processes the specified interface message with the `INTF_MESSAGE_REQUEST_SET_HUMANIZER_LEVEL` message ID.
*/
static void process_message_request_set_humanizer_level( intf_header_t const * header, void const * payload );

/**
* @fn process_message_request_set_invert_paddles( intf_header_t const *, void const * )
* @brief Processes the specified interface message with the `INTF_MESSAGE_REQUEST_SET_INVERT_PADDLES` message ID.
Expand Down Expand Up @@ -443,6 +455,10 @@ static void process_message( intf_header_t const * header, void const * payload
process_message_request_get_buzzer_frequency( header, payload );
break;

case INTF_MESSAGE_REQUEST_GET_HUMANIZER_LEVEL:
process_message_request_get_humanizer_level( header, payload );
break;

case INTF_MESSAGE_REQUEST_GET_INVERT_PADDLES:
process_message_request_get_invert_paddles( header, payload );
break;
Expand Down Expand Up @@ -511,6 +527,10 @@ static void process_message( intf_header_t const * header, void const * payload
process_message_request_set_buzzer_frequency( header, payload );
break;

case INTF_MESSAGE_REQUEST_SET_HUMANIZER_LEVEL:
process_message_request_set_humanizer_level( header, payload );
break;

case INTF_MESSAGE_REQUEST_SET_INVERT_PADDLES:
process_message_request_set_invert_paddles( header, payload );
break;
Expand Down Expand Up @@ -658,6 +678,17 @@ static void process_message_request_get_buzzer_frequency( intf_header_t const *
} /* process_message_request_get_buzzer_frequency() */


static void process_message_request_get_humanizer_level( intf_header_t const * header, void const * payload )
{
( void )payload;
VALIDATE_PAYLOAD_SIZE_OR_BAIL( 0 );

float level = keyer_get_humanizer_level();
send_packet( INTF_MESSAGE_REPLY_SUCCESS, & level, sizeof( level ) );

} /* process_message_request_get_humanizer_level() */


static void process_message_request_get_invert_paddles( intf_header_t const * header, void const * payload )
{
( void )payload;
Expand Down Expand Up @@ -875,6 +906,19 @@ static void process_message_request_set_buzzer_frequency( intf_header_t const *
} /* process_message_request_set_buzzer_frequency() */


static void process_message_request_set_humanizer_level( intf_header_t const * header, void const * payload )
{
VALIDATE_PAYLOAD_SIZE_OR_BAIL( sizeof( float ) );

float level = *( ( float const * )payload );
VALIDATE_RANGE_OR_BAIL( level, KEYER_HUMANIZER_LEVEL_MIN, KEYER_HUMANIZER_LEVEL_MAX );

keyer_set_humanizer_level( level );
send_empty_packet( INTF_MESSAGE_REPLY_SUCCESS );

} /* process_message_request_set_humanizer_level() */


static void process_message_request_set_invert_paddles( intf_header_t const * header, void const * payload )
{
VALIDATE_PAYLOAD_SIZE_OR_BAIL( sizeof( bool ) );
Expand Down
2 changes: 2 additions & 0 deletions src/main/application/intf_types.h
Original file line number Diff line number Diff line change
Expand Up @@ -49,6 +49,7 @@ enum
INTF_MESSAGE_REQUEST_AUTOKEY_QUICK_MSG, /**< Queues a quick message to be autokeyed.*/
INTF_MESSAGE_REQUEST_GET_BUZZER_ENABLED,/**< Get buzzer enablement. */
INTF_MESSAGE_REQUEST_GET_BUZZER_FREQUENCY,/**< Get buzzer frequency. */
INTF_MESSAGE_REQUEST_GET_HUMANIZER_LEVEL,/**< Get humanizer level. */
INTF_MESSAGE_REQUEST_GET_INVERT_PADDLES,/**< Gets paddle inversion setting. */
INTF_MESSAGE_REQUEST_GET_IO_POLARITY, /**< Gets I/O pin polarity. */
INTF_MESSAGE_REQUEST_GET_IO_STATE, /**< Gets I/O pin state. */
Expand All @@ -66,6 +67,7 @@ enum
INTF_MESSAGE_REQUEST_RESTORE_DEFAULT_CONFIG,/**< Restores default configuration. */
INTF_MESSAGE_REQUEST_SET_BUZZER_ENABLED,/**< Enable or disable buzzer. */
INTF_MESSAGE_REQUEST_SET_BUZZER_FREQUENCY,/**< Set buzzer frequency. */
INTF_MESSAGE_REQUEST_SET_HUMANIZER_LEVEL,/**< Set humanizer level. */
INTF_MESSAGE_REQUEST_SET_INVERT_PADDLES,/**< Sets paddle inversion setting. */
INTF_MESSAGE_REQUEST_SET_IO_POLARITY, /**< Sets I/O pin polarity. */
INTF_MESSAGE_REQUEST_SET_IO_TYPE, /**< Sets I/O pin type. */
Expand Down
72 changes: 59 additions & 13 deletions src/main/application/keyer.c
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@

/* ---------------------------------------------------- INCLUDES ---------------------------------------------------- */

#include <math.h>
#include <stdbool.h>
#include <stdint.h>
#include <stdlib.h>
Expand Down Expand Up @@ -66,6 +67,7 @@ enum
static bool s_keyed = false; /**< Is the keyer hardware currently keyed? */
static bool s_panicked = false; /**< Was the keyer panic activated? */
static bool s_trainer_mode = false; /**< Is trainer mode on? */
static float s_humanizer_level = KEYER_HUMANIZER_OFF;/**< Humanizer level. */

static state_t s_state = STATE_OFF; /**< Currently active keyer state. */

Expand Down Expand Up @@ -188,6 +190,12 @@ static bool get_keyed( void );
*/
static state_t get_next_state( void );

/**
* @fn humanize_delay( tick_t )
* @brief Applies a random variation to the specified delay.
*/
static tick_t humanize_delay( tick_t delay );

/**
* @fn set_keyed( bool )
* @brief Sets whether the keyer hardware is keying or not.
Expand Down Expand Up @@ -253,6 +261,13 @@ size_t keyer_autokey_str_ex( char const * str, keyer_autokey_flag_field_t flags
} /* keyer_autokey_str() */


float keyer_get_humanizer_level( void )
{
return( s_humanizer_level );

} /* keyer_get_humanizer_level() */


bool keyer_get_on( void )
{
return( get_keyed() );
Expand Down Expand Up @@ -287,6 +302,7 @@ void keyer_init( void )
s_keyed = false;
s_panicked = false;
s_trainer_mode = false;
s_humanizer_level = KEYER_HUMANIZER_OFF;
s_state = STATE_OFF;
s_el = WPM_ELEMENT_NONE;
s_lockout_el = WPM_ELEMENT_NONE;
Expand Down Expand Up @@ -316,6 +332,13 @@ void keyer_panic( void )
} /* keyer_panic() */


void keyer_set_humanizer_level( float level )
{
s_humanizer_level = clamp( level, KEYER_HUMANIZER_LEVEL_MIN, KEYER_HUMANIZER_LEVEL_MAX );

} /* keyer_set_humanizer_level() */


void keyer_set_paddle_invert( bool invert )
{
config_t config;
Expand Down Expand Up @@ -877,21 +900,24 @@ static void do_state_autokey( tick_t tick, bool new_state )
if( wpm_element_is_keyed( s_el ) )
{
s_lockout_el = s_el;
s_el_stop_tick = tick + s_ticks[ s_el ];
s_el_stop_tick = tick
+ humanize_delay( s_ticks[ s_el ] );
s_el_stop_tick_vld = true;
s_el_start_tick = tick + s_ticks[ s_el ] + s_ticks[ WPM_ELEMENT_ELEMENT_SPACE ];
s_el_start_tick = tick
+ humanize_delay( s_ticks[ s_el ] + s_ticks[ WPM_ELEMENT_ELEMENT_SPACE ] );
s_el_start_tick_vld = true;
set_keyed( true );
}
else
{
s_el_stop_tick = 0;
s_el_stop_tick_vld = false;
s_el_start_tick = tick + s_ticks[ s_el ]
- ( prev_lockout_el_was_keyed ?
s_ticks[ WPM_ELEMENT_ELEMENT_SPACE ] : 0 )
- ( prev_el_was_letter_space ?
( s_ticks[ WPM_ELEMENT_LETTER_SPACE ] - s_ticks[ WPM_ELEMENT_ELEMENT_SPACE ] ) : 0 );
s_el_start_tick = tick
+ humanize_delay( s_ticks[ s_el ]
- ( prev_lockout_el_was_keyed ?
s_ticks[ WPM_ELEMENT_ELEMENT_SPACE ] : 0 )
- ( prev_el_was_letter_space ?
( s_ticks[ WPM_ELEMENT_LETTER_SPACE ] - s_ticks[ WPM_ELEMENT_ELEMENT_SPACE ] ) : 0 ) );
s_el_start_tick_vld = true;
}
}
Expand All @@ -913,9 +939,11 @@ static void do_state_dashes( tick_t tick, bool new_state )
// Activate keyer hardware
s_el = WPM_ELEMENT_DASH;
s_lockout_el = s_el;
s_el_stop_tick = tick + s_ticks[ WPM_ELEMENT_DASH ];
s_el_stop_tick = tick
+ humanize_delay( s_ticks[ WPM_ELEMENT_DASH ] );
s_el_stop_tick_vld = true;
s_el_start_tick = tick + s_ticks[ WPM_ELEMENT_DASH ] + s_ticks[ WPM_ELEMENT_ELEMENT_SPACE ];
s_el_start_tick = tick
+ humanize_delay( s_ticks[ WPM_ELEMENT_DASH ] + s_ticks[ WPM_ELEMENT_ELEMENT_SPACE ] );
s_el_start_tick_vld = true;
set_keyed( true );
}
Expand All @@ -937,9 +965,11 @@ static void do_state_dots( tick_t tick, bool new_state )
// Activate keyer hardware
s_el = WPM_ELEMENT_DOT;
s_lockout_el = s_el;
s_el_stop_tick = tick + s_ticks[ WPM_ELEMENT_DOT ];
s_el_stop_tick = tick
+ humanize_delay( s_ticks[ WPM_ELEMENT_DOT ] );
s_el_stop_tick_vld = true;
s_el_start_tick = tick + s_ticks[ WPM_ELEMENT_DOT ] + s_ticks[ WPM_ELEMENT_ELEMENT_SPACE ];
s_el_start_tick = tick
+ humanize_delay( s_ticks[ WPM_ELEMENT_DOT ] + s_ticks[ WPM_ELEMENT_ELEMENT_SPACE ] );
s_el_start_tick_vld = true;
set_keyed( true );
}
Expand All @@ -961,9 +991,11 @@ static void do_state_interleaved( tick_t tick, bool new_state )
// Activate keyer hardware
s_el = ( s_lockout_el == WPM_ELEMENT_DOT ? WPM_ELEMENT_DASH : WPM_ELEMENT_DOT );
s_lockout_el = s_el;
s_el_stop_tick = tick + s_ticks[ s_el ];
s_el_stop_tick = tick
+ humanize_delay( s_ticks[ s_el ] );
s_el_stop_tick_vld = true;
s_el_start_tick = tick + s_ticks[ s_el ] + s_ticks[ WPM_ELEMENT_ELEMENT_SPACE ];
s_el_start_tick = tick
+ humanize_delay( s_ticks[ s_el ] + s_ticks[ WPM_ELEMENT_ELEMENT_SPACE ] );
s_el_start_tick_vld = true;
set_keyed( true );
}
Expand Down Expand Up @@ -1113,6 +1145,20 @@ static state_t get_next_state( void )
} /* get_next_state() */


static tick_t humanize_delay( tick_t delay )
{
// Early check to avoid expensive floating point operations if not needed
if( s_humanizer_level == KEYER_HUMANIZER_OFF )
return( delay );

float rand = ( float )random() / ( float )RANDOM_MAX;
tick_t offset = ( tick_t )roundf( ( rand * s_humanizer_level ) * ( 0.5f * s_ticks[ WPM_ELEMENT_DOT ] ) );

return( delay + offset );

} /* humanize_delay() */


static void set_keyed( bool keyed )
{
s_keyed = keyed;
Expand Down
Loading