diff --git a/celrs/crsf.c b/celrs/crsf.c index ced13cc..0efd3f9 100644 --- a/celrs/crsf.c +++ b/celrs/crsf.c @@ -124,28 +124,34 @@ int16_t cel_crsf_channel_clamp(int16_t value) { } int16_t cel_crsf_channel_us_to_val(uint16_t us) { - /* TODO: convert microseconds to 11-bit channel value. - * Linear mapping: 988us -> 172, 2012us -> 1811. - * value = (us - 988) * (1811 - 172) / (2012 - 988) + 172 - * Clamp result to [CEL_CRSF_CH_MIN, CEL_CRSF_CH_MAX]. */ - (void)us; - return CEL_CRSF_CH_MID; + /* Clamp to valid range first to avoid underflow */ + if (us < 988) us = 988; + if (us > 2012) us = 2012; + /* Round to nearest: add half the divisor before dividing */ + int16_t value = (int16_t)( + ((us - 988) * (CEL_CRSF_CH_MAX - CEL_CRSF_CH_MIN) + + (2012 - 988) / 2) / (2012 - 988) + + CEL_CRSF_CH_MIN); + return cel_crsf_channel_clamp(value); } uint16_t cel_crsf_channel_val_to_us(int16_t value) { - /* TODO: convert 11-bit channel value to microseconds. - * Linear mapping: 172 -> 988us, 1811 -> 2012us. - * us = (value - 172) * (2012 - 988) / (1811 - 172) + 988 */ - (void)value; - return 1500; + value = cel_crsf_channel_clamp(value); + return (uint16_t)( + ((value - CEL_CRSF_CH_MIN) * (2012 - 988) + + (CEL_CRSF_CH_MAX - CEL_CRSF_CH_MIN) / 2) + / (CEL_CRSF_CH_MAX - CEL_CRSF_CH_MIN) + 988); } void cel_crsf_channel_default(int16_t channels[16]) { - /* TODO: fill with safe/disarmed values. - * CH1-Roll, CH2-Pitch, CH4-Yaw = CEL_CRSF_CH_MID (centered) - * CH3-Throttle = CEL_CRSF_CH_MIN (zero) - * CH5..CH16 (AUX) = CEL_CRSF_CH_MIN (off) */ - (void)channels; + memset(channels, 0, sizeof(int16_t) * 16); + channels[0] = CEL_CRSF_CH_MID; /* roll */ + channels[1] = CEL_CRSF_CH_MID; /* pitch */ + channels[2] = CEL_CRSF_CH_MIN; /* throttle */ + channels[3] = CEL_CRSF_CH_MID; /* yaw */ + for (int i = 4; i < 16; i++) { + channels[i] = CEL_CRSF_CH_MIN; + } } /* --------------------------------------------------------------------------- */ diff --git a/tests/test_crsf.c b/tests/test_crsf.c index e1199d5..df2030f 100644 --- a/tests/test_crsf.c +++ b/tests/test_crsf.c @@ -38,6 +38,60 @@ void test_channel_clamp_mid(void) { TEST_ASSERT_EQUAL_INT16(CEL_CRSF_CH_MID, cel_crsf_channel_clamp(CEL_CRSF_CH_MID)); } +void test_channel_us_to_val_min(void) { + TEST_ASSERT_EQUAL_INT16(CEL_CRSF_CH_MIN, cel_crsf_channel_us_to_val(988)); +} + +void test_channel_us_to_val_mid(void) { + TEST_ASSERT_EQUAL_INT16(CEL_CRSF_CH_MID, cel_crsf_channel_us_to_val(1500)); +} + +void test_channel_us_to_val_max(void) { + TEST_ASSERT_EQUAL_INT16(CEL_CRSF_CH_MAX, cel_crsf_channel_us_to_val(2012)); +} + +void test_channel_us_to_val_below_min(void) { + TEST_ASSERT_EQUAL_INT16(CEL_CRSF_CH_MIN, cel_crsf_channel_us_to_val(0)); +} + +void test_channel_us_to_val_above_max(void) { + TEST_ASSERT_EQUAL_INT16(CEL_CRSF_CH_MAX, cel_crsf_channel_us_to_val(65535)); +} + +void test_channel_val_to_us_min(void) { + TEST_ASSERT_EQUAL_UINT16(988, cel_crsf_channel_val_to_us(CEL_CRSF_CH_MIN)); +} + +void test_channel_val_to_us_mid(void) { + TEST_ASSERT_EQUAL_UINT16(1500, cel_crsf_channel_val_to_us(CEL_CRSF_CH_MID)); +} + +void test_channel_val_to_us_max(void) { + TEST_ASSERT_EQUAL_UINT16(2012, cel_crsf_channel_val_to_us(CEL_CRSF_CH_MAX)); +} + +void test_channel_default_throttle_min(void) { + int16_t ch[16]; + cel_crsf_channel_default(ch); + TEST_ASSERT_EQUAL_INT16(CEL_CRSF_CH_MIN, ch[2]); /* throttle */ +} + +void test_channel_default_centered(void) { + int16_t ch[16]; + cel_crsf_channel_default(ch); + TEST_ASSERT_EQUAL_INT16(CEL_CRSF_CH_MID, ch[0]); /* roll */ + TEST_ASSERT_EQUAL_INT16(CEL_CRSF_CH_MID, ch[1]); /* pitch */ + TEST_ASSERT_EQUAL_INT16(CEL_CRSF_CH_MID, ch[3]); /* yaw */ +} + +void test_channel_default_aux_min(void) { + int16_t ch[16]; + cel_crsf_channel_default(ch); + for (int i = 4; i < 16; i++) { + TEST_ASSERT_EQUAL_INT16(CEL_CRSF_CH_MIN, ch[i]); + } +} + /* Frame parse tests — ELRS format: [addr][length][type][payload][crc] */ /* Build a valid test frame with known CRC */ @@ -123,6 +177,17 @@ int main(void) { RUN_TEST(test_channel_clamp_min); RUN_TEST(test_channel_clamp_max); RUN_TEST(test_channel_clamp_mid); + RUN_TEST(test_channel_us_to_val_min); + RUN_TEST(test_channel_us_to_val_mid); + RUN_TEST(test_channel_us_to_val_max); + RUN_TEST(test_channel_us_to_val_below_min); + RUN_TEST(test_channel_us_to_val_above_max); + RUN_TEST(test_channel_val_to_us_min); + RUN_TEST(test_channel_val_to_us_mid); + RUN_TEST(test_channel_val_to_us_max); + RUN_TEST(test_channel_default_throttle_min); + RUN_TEST(test_channel_default_centered); + RUN_TEST(test_channel_default_aux_min); RUN_TEST(test_parse_valid_frame); RUN_TEST(test_parse_null_frame); RUN_TEST(test_parse_null_buf);