diff --git a/include/tone.h b/include/tone.h index 66bb0f445b03..bf238439a6fd 100644 --- a/include/tone.h +++ b/include/tone.h @@ -25,9 +25,12 @@ * @brief Generates one full pulse-code modulation (PCM) period of a tone with the * given parameters. * + * @note The returned frequency of the tone is the quotient of the sample_freq_hz + * divided by the tone_freq_hz. + * * @param tone User provided buffer. Must be large enough to hold * the generated PCM tone, depending on settings. - * @param tone_size Resulting tone size. + * @param tone_size Resulting tone size in bytes. * @param tone_freq_hz The desired tone frequency in the range [100..10000] Hz. * @param smpl_freq_hz Sampling frequency. * @param amplitude Amplitude in the range [0..1]. @@ -40,6 +43,28 @@ int tone_gen(int16_t *tone, size_t *tone_size, uint16_t tone_freq_hz, uint32_t smpl_freq_hz, float amplitude); +/** + * @brief Generates one full pulse-code modulation (PCM) period of a tone with the + * given parameters. + * + * @note The returned frequency of the tone is the quotient of the sample_freq_hz + * divided by the tone_freq_hz. + * @note The routine returns the tone samples left aligned in the carrier. + * + * @param tone User provided buffer. Must be large enough to hold + * the generated PCM tone, depending on settings. + * @param tone_size Resulting tone size in bytes. + * @param tone_freq_hz The desired tone frequency in the range [100..10000] Hz. + * @param sample_freq_hz Sampling frequency. + * @param sample_bits Number of bits to represent a sample (i.e. 8, 16, 24 or 32 bits). + * @param carrier_bits Number of bits to carry a sample (i.e. 8, 16 or 32 bit). + * @param amplitude Amplitude in the range [0..1]. + * + * @return 0 if successful, error otherwise. + */ +int tone_gen_size(void *tone, size_t *tone_size, uint16_t tone_freq_hz, uint32_t sample_freq_hz, + uint8_t sample_bits, uint8_t carrier_bits, float amplitude); + /** * @} */ diff --git a/lib/tone/tone.c b/lib/tone/tone.c index 206906bfb599..4a054c34f0c7 100644 --- a/lib/tone/tone.c +++ b/lib/tone/tone.c @@ -11,7 +11,7 @@ #include #include -#define FREQ_LIMIT_LOW 100 +#define FREQ_LIMIT_LOW 100 #define FREQ_LIMIT_HIGH 10000 int tone_gen(int16_t *tone, size_t *tone_size, uint16_t tone_freq_hz, uint32_t smpl_freq_hz, @@ -31,7 +31,7 @@ int tone_gen(int16_t *tone, size_t *tone_size, uint16_t tone_freq_hz, uint32_t s uint32_t samples_for_one_period = smpl_freq_hz / tone_freq_hz; - for (uint32_t i = 0; i < samples_for_one_period; i++) { + for (size_t i = 0; i < samples_for_one_period; i++) { float curr_val = i * 2 * PI / samples_for_one_period; float32_t res = arm_sin_f32(curr_val); /* Generate one sine wave */ @@ -43,3 +43,122 @@ int tone_gen(int16_t *tone, size_t *tone_size, uint16_t tone_freq_hz, uint32_t s return 0; } + +static int tone_gen_8(int8_t *tone, size_t *tone_size, uint16_t tone_freq_hz, uint32_t smpl_freq_hz, + uint8_t scale, uint32_t max, float amplitude) +{ + uint32_t samples_for_one_period = smpl_freq_hz / tone_freq_hz; + + tone[0] = 0; + + for (size_t i = 1, j = samples_for_one_period - 1; i <= samples_for_one_period / 2; + i++, j--) { + float curr_val = i * 2 * PI / samples_for_one_period; + float32_t res = arm_sin_f32(curr_val); + /* Generate one sine wave */ + tone[i] = amplitude * res * max; + tone[i] <<= scale; + tone[j] = -tone[i]; + } + + *tone_size = (size_t)samples_for_one_period * sizeof(int8_t); + + return 0; +} + +static int tone_gen_16(int16_t *tone, size_t *tone_size, uint16_t tone_freq_hz, + uint32_t smpl_freq_hz, uint8_t scale, uint32_t max, float amplitude) +{ + uint32_t samples_for_one_period = smpl_freq_hz / tone_freq_hz; + + tone[0] = 0; + + for (size_t i = 1, j = samples_for_one_period - 1; i <= samples_for_one_period / 2; + i++, j--) { + float curr_val = i * 2 * PI / samples_for_one_period; + float32_t res = arm_sin_f32(curr_val); + /* Generate one sine wave */ + tone[i] = amplitude * res * max; + tone[i] <<= scale; + tone[j] = -tone[i]; + } + + *tone_size = (size_t)samples_for_one_period * sizeof(int16_t); + + return 0; +} + +static int tone_gen_32(int32_t *tone, size_t *tone_size, uint16_t tone_freq_hz, + uint32_t smpl_freq_hz, uint8_t scale, int32_t max, float amplitude) +{ + uint32_t samples_for_one_period = smpl_freq_hz / tone_freq_hz; + + tone[0] = 0; + + for (size_t i = 1, j = samples_for_one_period - 1; i <= samples_for_one_period / 2; + i++, j--) { + float curr_val = i * 2 * PI / samples_for_one_period; + float32_t res = arm_sin_f32(curr_val); + /* Generate one sine wave */ + tone[i] = amplitude * res * max; + tone[i] <<= scale; + tone[j] = -tone[i]; + } + + *tone_size = (size_t)samples_for_one_period * sizeof(int32_t); + + return 0; +} + +int tone_gen_size(void *tone, size_t *tone_size, uint16_t tone_freq_hz, uint32_t sample_freq_hz, + uint8_t sample_bits, uint8_t carrier_bits, float amplitude) +{ + int ret; + uint8_t scale; + int32_t max; + + if (tone == NULL || tone_size == NULL) { + return -ENXIO; + } + + if (!sample_freq_hz || tone_freq_hz < FREQ_LIMIT_LOW || tone_freq_hz > FREQ_LIMIT_HIGH) { + return -EINVAL; + } + + if (amplitude > 1 || amplitude <= 0) { + return -EPERM; + } + + if (!sample_bits || !carrier_bits || sample_bits > carrier_bits) { + return -EINVAL; + } + + if (sample_bits == 8) { + max = INT8_MAX; + } else if (sample_bits == 16) { + max = INT16_MAX; + } else if (sample_bits == 24) { + max = 0x007FFFFF; + } else if (sample_bits == 32) { + max = INT32_MAX; + } else { + return -EINVAL; + } + + scale = carrier_bits - sample_bits; + + if (carrier_bits == 8) { + ret = tone_gen_8((int8_t *)tone, tone_size, tone_freq_hz, sample_freq_hz, scale, + max, amplitude); + } else if (carrier_bits == 16) { + ret = tone_gen_16((int16_t *)tone, tone_size, tone_freq_hz, sample_freq_hz, scale, + max, amplitude); + } else if (carrier_bits == 32) { + ret = tone_gen_32((int32_t *)tone, tone_size, tone_freq_hz, sample_freq_hz, scale, + max, amplitude); + } else { + return -EINVAL; + } + + return 0; +} diff --git a/tests/lib/tone/src/main.c b/tests/lib/tone/src/main.c index 82708504b7ab..6ab193f6ffe3 100644 --- a/tests/lib/tone/src/main.c +++ b/tests/lib/tone/src/main.c @@ -9,23 +9,79 @@ #include #include -static int32_t tone_sum(int16_t *tone, size_t size) +static int32_t tone_sum_8(int8_t *tone, size_t size) { int32_t sum = 0; - for (size_t i = 0; i < size / 2; i++) { + for (size_t i = 0; i < size / sizeof(int8_t); i++) { sum += tone[i]; } return sum; } -static void tone_high_low_idx(int16_t *tone, size_t size, uint32_t *idx_low, uint32_t *idx_high) +static int32_t tone_sum_16(int16_t *tone, size_t size) +{ + int32_t sum = 0; + + for (size_t i = 0; i < size / sizeof(int16_t); i++) { + sum += tone[i]; + } + + return sum; +} + +static int64_t tone_sum_32(int32_t *tone, size_t size) +{ + int64_t sum = 0; + + for (size_t i = 0; i < size / sizeof(int32_t); i++) { + sum += tone[i]; + } + + return sum; +} + +static void tone_high_low_idx_8(int8_t *tone, size_t size, uint32_t *idx_low, uint32_t *idx_high) +{ + int8_t highest = 0; + int8_t lowest = 0; + + for (size_t i = 0; i < size / sizeof(int8_t); i++) { + if (tone[i] < lowest) { + lowest = tone[i]; + *idx_low = i; + } + if (tone[i] > highest) { + highest = tone[i]; + *idx_high = i; + } + } +} + +static void tone_high_low_idx_16(int16_t *tone, size_t size, uint32_t *idx_low, uint32_t *idx_high) { int16_t highest = 0; int16_t lowest = 0; - for (size_t i = 0; i < size / 2; i++) { + for (size_t i = 0; i < size / sizeof(int16_t); i++) { + if (tone[i] < lowest) { + lowest = tone[i]; + *idx_low = i; + } + if (tone[i] > highest) { + highest = tone[i]; + *idx_high = i; + } + } +} + +static void tone_high_low_idx_32(int32_t *tone, size_t size, uint32_t *idx_low, uint32_t *idx_high) +{ + int32_t highest = 0; + int32_t lowest = 0; + + for (size_t i = 0; i < size / sizeof(int32_t); i++) { if (tone[i] < lowest) { lowest = tone[i]; *idx_low = i; @@ -41,13 +97,13 @@ static void tone_high_low_idx(int16_t *tone, size_t size, uint32_t *idx_low, uin ZTEST(suite_tone, test_tone_gen_valid) { #define NUM_TESTS 3 - uint16_t freq[] = { 100, 480, 960 }; - uint32_t smpl_freq[] = { 10000, 48000, 48000 }; - size_t tone_size_desired[] = { 200, 200, 100 }; - float amplitude[] = { 1, 1, 1 }; + uint16_t freq[] = {100, 480, 960}; + uint32_t smpl_freq[] = {10000, 48000, 48000}; + size_t tone_size_desired[] = {200, 200, 100}; + float amplitude[] = {1, 1, 1}; for (uint8_t i = 0; i < NUM_TESTS; i++) { - int16_t tone[400] = { 0 }; + int16_t tone[400] = {0}; size_t tone_size = 0; uint32_t idx_low = 0; uint32_t idx_high = 0; @@ -58,9 +114,9 @@ ZTEST(suite_tone, test_tone_gen_valid) /* Since a single period is generated, the center sample should always be zero */ zassert_equal(tone[tone_size / 2], 0, "Center sample not zero"); - zassert_equal(tone_sum(tone, tone_size), 0, "The sum of samples are not zero"); + zassert_equal(tone_sum_16(tone, tone_size), 0, "The sum of samples are not zero"); - tone_high_low_idx(tone, tone_size, &idx_low, &idx_high); + tone_high_low_idx_16(tone, tone_size, &idx_low, &idx_high); zassert_equal(idx_low, 3 * (tone_size / 2) / 4, "Lowest sample not at the 3/4 mark"); zassert_equal(idx_high, (tone_size / 2) / 4, "Highest sample not at the 1/4 mark"); @@ -69,7 +125,7 @@ ZTEST(suite_tone, test_tone_gen_valid) ZTEST(suite_tone, test_illegal_args) { - int16_t tone[200] = { 0 }; + int16_t tone[200] = {0}; size_t tone_size; uint16_t freq = 100; uint32_t smpl_freq = 10000; @@ -99,4 +155,180 @@ ZTEST(suite_tone, test_illegal_args) "Err code returned"); } +ZTEST(suite_tone_gen_size, test_tone_gen_size_8_valid) +{ + uint16_t freq[] = {100, 480, 960}; + uint32_t sample_freq[] = {10000, 48000, 48000}; + float amplitude = 1; + uint8_t sample_width = 8; + uint8_t carrier_bits = 8; + uint32_t idx_low = 0; + uint32_t idx_high = 0; + + for (size_t i = 0; i < ARRAY_SIZE(freq); i++) { + uint8_t tone[100]; + size_t tone_size = 0; + size_t tone_size_desired = (sample_freq[i] / freq[i]) * (carrier_bits / 8); + + memset(tone, 0, sizeof(tone)); + + zassert_equal(tone_gen_size(tone, &tone_size, freq[i], sample_freq[i], sample_width, + carrier_bits, amplitude), + 0, "Err code returned"); + zassert_equal(tone_size, tone_size_desired, "Incorrect tone size"); + + /* Since a single period is generated, the center sample should always be + * zero + */ + zassert_equal(tone[tone_size / (2 * sizeof(int8_t))], 0, "Center sample not zero"); + zassert_equal(tone_sum_8(tone, tone_size), 0, "The sum of samples are not zero"); + + tone_high_low_idx_8(tone, tone_size, &idx_low, &idx_high); + zassert_equal(idx_low, 3 * tone_size / 4, "Lowest sample not at the 3/4 mark"); + zassert_equal(idx_high, (tone_size / sizeof(int8_t)) / 4, + "Highest sample not at the 1/4 mark"); + } +} + +ZTEST(suite_tone_gen_size, test_tone_gen_size_16_valid) +{ + uint16_t freq[] = {100, 480, 960}; + uint32_t sample_freq[] = {10000, 48000, 48000}; + uint8_t sample_width[] = {8, 16}; + float amplitude = 1; + uint8_t carrier_bits = 16; + uint32_t idx_low = 0; + uint32_t idx_high = 0; + + for (size_t j = 0; j < ARRAY_SIZE(sample_width); j++) { + for (size_t i = 0; i < ARRAY_SIZE(freq); i++) { + uint16_t tone[100]; + size_t tone_size = 0; + size_t tone_size_desired = (sample_freq[i] / freq[i]) * (carrier_bits / 8); + + memset(tone, 0, sizeof(tone)); + + zassert_equal(tone_gen_size(tone, &tone_size, freq[i], sample_freq[i], + sample_width[j], carrier_bits, amplitude), + 0, "Err code returned"); + zassert_equal(tone_size, tone_size_desired, "Incorrect tone size"); + + /* Since a single period is generated, the center sample should + * always be zero + */ + zassert_equal(tone[tone_size / (2 * sizeof(int16_t))], 0, + "Center sample not zero"); + zassert_equal(tone_sum_16(tone, tone_size), 0, + "The sum of samples are not zero"); + + tone_high_low_idx_16(tone, tone_size, &idx_low, &idx_high); + zassert_equal(idx_low, 3 * (tone_size / 2) / 4, + "Lowest sample not at the 3/4 mark"); + zassert_equal(idx_high, (tone_size / sizeof(int16_t)) / 4, + "Highest sample not at the 1/4 mark"); + } + } +} + +ZTEST(suite_tone_gen_size, test_tone_gen_size_32_valid) +{ + uint16_t freq[] = {111, 480, 960}; + uint32_t sample_freq[] = {10000, 48000, 48000}; + uint8_t sample_width[] = {32}; + uint8_t carrier_bits = 32; + float amplitude = 1; + uint32_t idx_low = 0; + uint32_t idx_high = 0; + + for (size_t j = 0; j < ARRAY_SIZE(sample_width); j++) { + for (size_t i = 0; i < ARRAY_SIZE(freq); i++) { + uint32_t tone[100]; + size_t tone_size = 0; + size_t tone_size_desired = (sample_freq[i] / freq[i]) * (carrier_bits / 8); + + memset(tone, 0, sizeof(tone)); + + zassert_equal(tone_gen_size(tone, &tone_size, freq[i], sample_freq[i], + sample_width[j], carrier_bits, amplitude), + 0, "Err code returned"); + zassert_equal(tone_size, tone_size_desired, "Incorrect tone size: %d (%d)", + tone_size, tone_size_desired); + + /* Since a single period is generated, the center sample should always be + * zero + */ + zassert_equal(tone[tone_size / (2 * sizeof(int32_t))], 0, + "Center sample not zero"); + zassert_equal(tone_sum_32(tone, tone_size), (int64_t)0, + "The sum of samples are not zero"); + + tone_high_low_idx_32(tone, tone_size, &idx_low, &idx_high); + zassert_equal(idx_low, 3 * (tone_size / sizeof(int32_t)) / 4, + "Lowest sample not at the 3/4 mark"); + zassert_equal(idx_high, (tone_size / sizeof(int32_t)) / 4, + "Highest sample not at the 1/4 mark"); + } + } +} + +ZTEST(suite_tone_gen_size, test_tone_gen_size_illegal_args) +{ + int16_t tone[200] = {0}; + size_t tone_size; + uint16_t freq = 100; + uint32_t smpl_freq = 10000; + uint8_t sample_bits = 16; + uint8_t carrier_bits = 32; + float amplitude = 1; + + /* NULL ptr */ + zassert_equal(tone_gen_size(NULL, &tone_size, freq, smpl_freq, sample_bits, carrier_bits, + amplitude), + -ENXIO, "Wrong code returned"); + /* NULL ptr */ + zassert_equal( + tone_gen_size(tone, NULL, freq, smpl_freq, sample_bits, carrier_bits, amplitude), + -ENXIO, "Wrong code returned"); + /* 0 freq */ + zassert_equal( + tone_gen_size(tone, &tone_size, 0, smpl_freq, sample_bits, carrier_bits, amplitude), + -EINVAL, "Wrong code returned"); + /* freq too low */ + zassert_equal(tone_gen_size(tone, &tone_size, 10, smpl_freq, sample_bits, carrier_bits, + amplitude), + -EINVAL, "Wrong code returned"); + /* freq too high */ + zassert_equal(tone_gen_size(tone, &tone_size, 10001, smpl_freq, sample_bits, carrier_bits, + amplitude), + -EINVAL, "Wrong code returned"); + /* smpl_freq 0 */ + zassert_equal( + tone_gen_size(tone, &tone_size, freq, 0, sample_bits, carrier_bits, amplitude), + -EINVAL, "Err code returned"); + /* sample_bits 0 */ + zassert_equal(tone_gen_size(tone, &tone_size, freq, smpl_freq, 0, carrier_bits, amplitude), + -EINVAL, "Err code returned"); + /* sample_bits not in set */ + zassert_equal(tone_gen_size(tone, &tone_size, freq, smpl_freq, 12, carrier_bits, amplitude), + -EINVAL, "Err code returned"); + /* carrier_bits 0 */ + zassert_equal(tone_gen_size(tone, &tone_size, freq, smpl_freq, sample_bits, 0, amplitude), + -EINVAL, "Err code returned"); + /* carrier_bits too small */ + zassert_equal(tone_gen_size(tone, &tone_size, freq, smpl_freq, sample_bits, 8, amplitude), + -EINVAL, "Err code returned"); + /* carrier_bits not in set */ + zassert_equal(tone_gen_size(tone, &tone_size, freq, smpl_freq, sample_bits, 26, amplitude), + -EINVAL, "Err code returned"); + /* 0 Amplitude */ + zassert_equal( + tone_gen_size(tone, &tone_size, freq, smpl_freq, sample_bits, carrier_bits, 0), + -EPERM, "Err code returned"); + /* Amplitude too high*/ + zassert_equal( + tone_gen_size(tone, &tone_size, freq, smpl_freq, sample_bits, carrier_bits, 1.1), + -EPERM, "Err code returned"); +} + ZTEST_SUITE(suite_tone, NULL, NULL, NULL, NULL, NULL); +ZTEST_SUITE(suite_tone_gen_size, NULL, NULL, NULL, NULL, NULL);