Skip to content

Commit 5aaa087

Browse files
committed
Add humanizer feature to make code sound more like a straight key.
1 parent 9d94c1f commit 5aaa087

File tree

8 files changed

+175
-16
lines changed

8 files changed

+175
-16
lines changed

scripts/autokey.py

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -187,6 +187,19 @@ def line_starts_with(x: str) -> bool:
187187
print(intf.version())
188188
continue
189189

190+
elif line_equals(':humanizer'):
191+
# Print humanizer level
192+
print(intf.get_humanizer_level())
193+
continue
194+
195+
elif line_starts_with(':humanizer'):
196+
# Set humanizer level
197+
try:
198+
intf.set_humanizer_level(float(line[10:]))
199+
except ValueError:
200+
print('Invalid level?')
201+
continue
202+
190203
elif line_starts_with(':'):
191204
# Unknown command?
192205
print('Unknown command?')

scripts/superkey/interface.py

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -170,6 +170,13 @@ def get_buzzer_frequency(self) -> int:
170170
self.__send_packet(MessageID.REQUEST_GET_BUZZER_FREQUENCY)
171171
return self.__check_reply('<H')[0]
172172

173+
def get_humanizer_level(self) -> float:
174+
"""
175+
Sends the `REQUEST_GET_HUMANIZER_LEVEL` command. Returns the current humanizer level as a fraction.
176+
"""
177+
self.__send_packet(MessageID.REQUEST_GET_HUMANIZER_LEVEL)
178+
return self.__check_reply('<f')[0]
179+
173180
def get_invert_paddles(self) -> bool:
174181
"""
175182
Sends the `REQUEST_GET_INVERT_PADDLES` command. Returns whether or not the paddles are inverted.
@@ -289,6 +296,13 @@ def set_buzzer_frequency(self, frequency: int):
289296
self.__send_packet(MessageID.REQUEST_SET_BUZZER_FREQUENCY, struct.pack('<H', frequency))
290297
self.__check_reply_empty()
291298

299+
def set_humanizer_level(self, level: float):
300+
"""
301+
Sends the `REQUEST_SET_HUMANIZER_LEVEL` command. Sets the humanizer level as a fraction.
302+
"""
303+
self.__send_packet(MessageID.REQUEST_SET_HUMANIZER_LEVEL, struct.pack('<f', level))
304+
self.__check_reply_empty()
305+
292306
def set_invert_paddles(self, inverted: bool):
293307
"""
294308
Sends the `REQUEST_SET_INVERT_PADDLES` command. Sets whether the paddles are inverted.

scripts/superkey/types.py

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -109,6 +109,7 @@
109109
'REQUEST_AUTOKEY_QUICK_MSG',
110110
'REQUEST_GET_BUZZER_ENABLED',
111111
'REQUEST_GET_BUZZER_FREQUENCY',
112+
'REQUEST_GET_HUMANIZER_LEVEL',
112113
'REQUEST_GET_INVERT_PADDLES',
113114
'REQUEST_GET_IO_POLARITY',
114115
'REQUEST_GET_IO_STATE',
@@ -126,6 +127,7 @@
126127
'REQUEST_RESTORE_DEFAULT_CONFIG',
127128
'REQUEST_SET_BUZZER_ENABLED',
128129
'REQUEST_SET_BUZZER_FREQUENCY',
130+
'REQUEST_SET_HUMANIZER_LEVEL',
129131
'REQUEST_SET_INVERT_PADDLES',
130132
'REQUEST_SET_IO_POLARITY',
131133
'REQUEST_SET_IO_TYPE',

scripts/wordcopy.py

Lines changed: 9 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,7 @@
2222
DEFAULT_WPM = 20.
2323
DEFAULT_MINLEN = 2
2424
DEFAULT_MAXLEN = 8
25+
DEFAULT_HUMANIZE = 0.
2526

2627
# ----------------------------------------------------- PROCEDURES -----------------------------------------------------
2728

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

4345
def _main(port: str,
@@ -47,7 +49,8 @@ def _main(port: str,
4749
delay: float,
4850
wpm: float,
4951
minlen: int,
50-
maxlen: int):
52+
maxlen: int,
53+
humanizer: float):
5154
"""
5255
Runs the trainer.
5356
"""
@@ -62,10 +65,12 @@ def _main(port: str,
6265
# Get initial settings
6366
initial_wpm = intf.get_wpm()
6467
initial_trainer_mode = intf.get_trainer_mode()
68+
initial_humanizer_level = intf.get_humanizer_level()
6569

6670
# Override settings
6771
intf.set_wpm(wpm)
6872
intf.set_trainer_mode(True)
73+
intf.set_humanizer_level(humanizer)
6974

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

9399

94100
# -------------------------------------------------- IMPORT PROCEDURE --------------------------------------------------
@@ -106,4 +112,5 @@ def _main(port: str,
106112
delay = args.delay,
107113
wpm = args.wpm,
108114
minlen = args.minlen,
109-
maxlen = args.maxlen)
115+
maxlen = args.maxlen,
116+
humanizer = args.humanizer)

src/main/application/intf_port.c

Lines changed: 44 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -140,6 +140,12 @@ static void process_message_request_get_buzzer_enabled( intf_header_t const * he
140140
*/
141141
static void process_message_request_get_buzzer_frequency( intf_header_t const * header, void const * payload );
142142

143+
/**
144+
* @fn process_message_request_get_humanizer_level( intf_header_t const *, void const * )
145+
* @brief Processes the specified interface message with the `INTF_MESSAGE_REQUEST_GET_HUMANIZER_LEVEL` message ID.
146+
*/
147+
static void process_message_request_get_humanizer_level( intf_header_t const * header, void const * payload );
148+
143149
/**
144150
* @fn process_message_request_get_invert_paddles( intf_header_t const *, void const * )
145151
* @brief Processes the specified interface message with the `INTF_MESSAGE_REQUEST_GET_INVERT_PADDLES` message ID.
@@ -242,6 +248,12 @@ static void process_message_request_set_buzzer_enabled( intf_header_t const * he
242248
*/
243249
static void process_message_request_set_buzzer_frequency( intf_header_t const * header, void const * payload );
244250

251+
/**
252+
* @fn process_message_request_set_humanizer_level( intf_header_t const *, void const * )
253+
* @brief Processes the specified interface message with the `INTF_MESSAGE_REQUEST_SET_HUMANIZER_LEVEL` message ID.
254+
*/
255+
static void process_message_request_set_humanizer_level( intf_header_t const * header, void const * payload );
256+
245257
/**
246258
* @fn process_message_request_set_invert_paddles( intf_header_t const *, void const * )
247259
* @brief Processes the specified interface message with the `INTF_MESSAGE_REQUEST_SET_INVERT_PADDLES` message ID.
@@ -443,6 +455,10 @@ static void process_message( intf_header_t const * header, void const * payload
443455
process_message_request_get_buzzer_frequency( header, payload );
444456
break;
445457

458+
case INTF_MESSAGE_REQUEST_GET_HUMANIZER_LEVEL:
459+
process_message_request_get_humanizer_level( header, payload );
460+
break;
461+
446462
case INTF_MESSAGE_REQUEST_GET_INVERT_PADDLES:
447463
process_message_request_get_invert_paddles( header, payload );
448464
break;
@@ -511,6 +527,10 @@ static void process_message( intf_header_t const * header, void const * payload
511527
process_message_request_set_buzzer_frequency( header, payload );
512528
break;
513529

530+
case INTF_MESSAGE_REQUEST_SET_HUMANIZER_LEVEL:
531+
process_message_request_set_humanizer_level( header, payload );
532+
break;
533+
514534
case INTF_MESSAGE_REQUEST_SET_INVERT_PADDLES:
515535
process_message_request_set_invert_paddles( header, payload );
516536
break;
@@ -658,6 +678,17 @@ static void process_message_request_get_buzzer_frequency( intf_header_t const *
658678
} /* process_message_request_get_buzzer_frequency() */
659679

660680

681+
static void process_message_request_get_humanizer_level( intf_header_t const * header, void const * payload )
682+
{
683+
( void )payload;
684+
VALIDATE_PAYLOAD_SIZE_OR_BAIL( 0 );
685+
686+
float level = keyer_get_humanizer_level();
687+
send_packet( INTF_MESSAGE_REPLY_SUCCESS, & level, sizeof( level ) );
688+
689+
} /* process_message_request_get_humanizer_level() */
690+
691+
661692
static void process_message_request_get_invert_paddles( intf_header_t const * header, void const * payload )
662693
{
663694
( void )payload;
@@ -875,6 +906,19 @@ static void process_message_request_set_buzzer_frequency( intf_header_t const *
875906
} /* process_message_request_set_buzzer_frequency() */
876907

877908

909+
static void process_message_request_set_humanizer_level( intf_header_t const * header, void const * payload )
910+
{
911+
VALIDATE_PAYLOAD_SIZE_OR_BAIL( sizeof( float ) );
912+
913+
float level = *( ( float const * )payload );
914+
VALIDATE_RANGE_OR_BAIL( level, KEYER_HUMANIZER_LEVEL_MIN, KEYER_HUMANIZER_LEVEL_MAX );
915+
916+
keyer_set_humanizer_level( level );
917+
send_empty_packet( INTF_MESSAGE_REPLY_SUCCESS );
918+
919+
} /* process_message_request_set_humanizer_level() */
920+
921+
878922
static void process_message_request_set_invert_paddles( intf_header_t const * header, void const * payload )
879923
{
880924
VALIDATE_PAYLOAD_SIZE_OR_BAIL( sizeof( bool ) );

src/main/application/intf_types.h

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -49,6 +49,7 @@ enum
4949
INTF_MESSAGE_REQUEST_AUTOKEY_QUICK_MSG, /**< Queues a quick message to be autokeyed.*/
5050
INTF_MESSAGE_REQUEST_GET_BUZZER_ENABLED,/**< Get buzzer enablement. */
5151
INTF_MESSAGE_REQUEST_GET_BUZZER_FREQUENCY,/**< Get buzzer frequency. */
52+
INTF_MESSAGE_REQUEST_GET_HUMANIZER_LEVEL,/**< Get humanizer level. */
5253
INTF_MESSAGE_REQUEST_GET_INVERT_PADDLES,/**< Gets paddle inversion setting. */
5354
INTF_MESSAGE_REQUEST_GET_IO_POLARITY, /**< Gets I/O pin polarity. */
5455
INTF_MESSAGE_REQUEST_GET_IO_STATE, /**< Gets I/O pin state. */
@@ -66,6 +67,7 @@ enum
6667
INTF_MESSAGE_REQUEST_RESTORE_DEFAULT_CONFIG,/**< Restores default configuration. */
6768
INTF_MESSAGE_REQUEST_SET_BUZZER_ENABLED,/**< Enable or disable buzzer. */
6869
INTF_MESSAGE_REQUEST_SET_BUZZER_FREQUENCY,/**< Set buzzer frequency. */
70+
INTF_MESSAGE_REQUEST_SET_HUMANIZER_LEVEL,/**< Set humanizer level. */
6971
INTF_MESSAGE_REQUEST_SET_INVERT_PADDLES,/**< Sets paddle inversion setting. */
7072
INTF_MESSAGE_REQUEST_SET_IO_POLARITY, /**< Sets I/O pin polarity. */
7173
INTF_MESSAGE_REQUEST_SET_IO_TYPE, /**< Sets I/O pin type. */

src/main/application/keyer.c

Lines changed: 59 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@
99

1010
/* ---------------------------------------------------- INCLUDES ---------------------------------------------------- */
1111

12+
#include <math.h>
1213
#include <stdbool.h>
1314
#include <stdint.h>
1415
#include <stdlib.h>
@@ -66,6 +67,7 @@ enum
6667
static bool s_keyed = false; /**< Is the keyer hardware currently keyed? */
6768
static bool s_panicked = false; /**< Was the keyer panic activated? */
6869
static bool s_trainer_mode = false; /**< Is trainer mode on? */
70+
static float s_humanizer_level = KEYER_HUMANIZER_OFF;/**< Humanizer level. */
6971

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

@@ -188,6 +190,12 @@ static bool get_keyed( void );
188190
*/
189191
static state_t get_next_state( void );
190192

193+
/**
194+
* @fn humanize_delay( tick_t )
195+
* @brief Applies a random variation to the specified delay.
196+
*/
197+
static tick_t humanize_delay( tick_t delay );
198+
191199
/**
192200
* @fn set_keyed( bool )
193201
* @brief Sets whether the keyer hardware is keying or not.
@@ -253,6 +261,13 @@ size_t keyer_autokey_str_ex( char const * str, keyer_autokey_flag_field_t flags
253261
} /* keyer_autokey_str() */
254262

255263

264+
float keyer_get_humanizer_level( void )
265+
{
266+
return( s_humanizer_level );
267+
268+
} /* keyer_get_humanizer_level() */
269+
270+
256271
bool keyer_get_on( void )
257272
{
258273
return( get_keyed() );
@@ -287,6 +302,7 @@ void keyer_init( void )
287302
s_keyed = false;
288303
s_panicked = false;
289304
s_trainer_mode = false;
305+
s_humanizer_level = KEYER_HUMANIZER_OFF;
290306
s_state = STATE_OFF;
291307
s_el = WPM_ELEMENT_NONE;
292308
s_lockout_el = WPM_ELEMENT_NONE;
@@ -316,6 +332,13 @@ void keyer_panic( void )
316332
} /* keyer_panic() */
317333

318334

335+
void keyer_set_humanizer_level( float level )
336+
{
337+
s_humanizer_level = clamp( level, KEYER_HUMANIZER_LEVEL_MIN, KEYER_HUMANIZER_LEVEL_MAX );
338+
339+
} /* keyer_set_humanizer_level() */
340+
341+
319342
void keyer_set_paddle_invert( bool invert )
320343
{
321344
config_t config;
@@ -877,21 +900,24 @@ static void do_state_autokey( tick_t tick, bool new_state )
877900
if( wpm_element_is_keyed( s_el ) )
878901
{
879902
s_lockout_el = s_el;
880-
s_el_stop_tick = tick + s_ticks[ s_el ];
903+
s_el_stop_tick = tick
904+
+ humanize_delay( s_ticks[ s_el ] );
881905
s_el_stop_tick_vld = true;
882-
s_el_start_tick = tick + s_ticks[ s_el ] + s_ticks[ WPM_ELEMENT_ELEMENT_SPACE ];
906+
s_el_start_tick = tick
907+
+ humanize_delay( s_ticks[ s_el ] + s_ticks[ WPM_ELEMENT_ELEMENT_SPACE ] );
883908
s_el_start_tick_vld = true;
884909
set_keyed( true );
885910
}
886911
else
887912
{
888913
s_el_stop_tick = 0;
889914
s_el_stop_tick_vld = false;
890-
s_el_start_tick = tick + s_ticks[ s_el ]
891-
- ( prev_lockout_el_was_keyed ?
892-
s_ticks[ WPM_ELEMENT_ELEMENT_SPACE ] : 0 )
893-
- ( prev_el_was_letter_space ?
894-
( s_ticks[ WPM_ELEMENT_LETTER_SPACE ] - s_ticks[ WPM_ELEMENT_ELEMENT_SPACE ] ) : 0 );
915+
s_el_start_tick = tick
916+
+ humanize_delay( s_ticks[ s_el ]
917+
- ( prev_lockout_el_was_keyed ?
918+
s_ticks[ WPM_ELEMENT_ELEMENT_SPACE ] : 0 )
919+
- ( prev_el_was_letter_space ?
920+
( s_ticks[ WPM_ELEMENT_LETTER_SPACE ] - s_ticks[ WPM_ELEMENT_ELEMENT_SPACE ] ) : 0 ) );
895921
s_el_start_tick_vld = true;
896922
}
897923
}
@@ -913,9 +939,11 @@ static void do_state_dashes( tick_t tick, bool new_state )
913939
// Activate keyer hardware
914940
s_el = WPM_ELEMENT_DASH;
915941
s_lockout_el = s_el;
916-
s_el_stop_tick = tick + s_ticks[ WPM_ELEMENT_DASH ];
942+
s_el_stop_tick = tick
943+
+ humanize_delay( s_ticks[ WPM_ELEMENT_DASH ] );
917944
s_el_stop_tick_vld = true;
918-
s_el_start_tick = tick + s_ticks[ WPM_ELEMENT_DASH ] + s_ticks[ WPM_ELEMENT_ELEMENT_SPACE ];
945+
s_el_start_tick = tick
946+
+ humanize_delay( s_ticks[ WPM_ELEMENT_DASH ] + s_ticks[ WPM_ELEMENT_ELEMENT_SPACE ] );
919947
s_el_start_tick_vld = true;
920948
set_keyed( true );
921949
}
@@ -937,9 +965,11 @@ static void do_state_dots( tick_t tick, bool new_state )
937965
// Activate keyer hardware
938966
s_el = WPM_ELEMENT_DOT;
939967
s_lockout_el = s_el;
940-
s_el_stop_tick = tick + s_ticks[ WPM_ELEMENT_DOT ];
968+
s_el_stop_tick = tick
969+
+ humanize_delay( s_ticks[ WPM_ELEMENT_DOT ] );
941970
s_el_stop_tick_vld = true;
942-
s_el_start_tick = tick + s_ticks[ WPM_ELEMENT_DOT ] + s_ticks[ WPM_ELEMENT_ELEMENT_SPACE ];
971+
s_el_start_tick = tick
972+
+ humanize_delay( s_ticks[ WPM_ELEMENT_DOT ] + s_ticks[ WPM_ELEMENT_ELEMENT_SPACE ] );
943973
s_el_start_tick_vld = true;
944974
set_keyed( true );
945975
}
@@ -961,9 +991,11 @@ static void do_state_interleaved( tick_t tick, bool new_state )
961991
// Activate keyer hardware
962992
s_el = ( s_lockout_el == WPM_ELEMENT_DOT ? WPM_ELEMENT_DASH : WPM_ELEMENT_DOT );
963993
s_lockout_el = s_el;
964-
s_el_stop_tick = tick + s_ticks[ s_el ];
994+
s_el_stop_tick = tick
995+
+ humanize_delay( s_ticks[ s_el ] );
965996
s_el_stop_tick_vld = true;
966-
s_el_start_tick = tick + s_ticks[ s_el ] + s_ticks[ WPM_ELEMENT_ELEMENT_SPACE ];
997+
s_el_start_tick = tick
998+
+ humanize_delay( s_ticks[ s_el ] + s_ticks[ WPM_ELEMENT_ELEMENT_SPACE ] );
967999
s_el_start_tick_vld = true;
9681000
set_keyed( true );
9691001
}
@@ -1113,6 +1145,20 @@ static state_t get_next_state( void )
11131145
} /* get_next_state() */
11141146

11151147

1148+
static tick_t humanize_delay( tick_t delay )
1149+
{
1150+
// Early check to avoid expensive floating point operations if not needed
1151+
if( s_humanizer_level == KEYER_HUMANIZER_OFF )
1152+
return( delay );
1153+
1154+
float rand = ( float )random() / ( float )RANDOM_MAX;
1155+
tick_t offset = ( tick_t )roundf( ( rand * s_humanizer_level ) * ( 0.5f * s_ticks[ WPM_ELEMENT_DOT ] ) );
1156+
1157+
return( delay + offset );
1158+
1159+
} /* humanize_delay() */
1160+
1161+
11161162
static void set_keyed( bool keyed )
11171163
{
11181164
s_keyed = keyed;

0 commit comments

Comments
 (0)