/* DSP functions * * (C) 2020 by Andreas Eversberg * All Rights Reserved * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ #define CHAN dsp->name #include #include #include #include #include #include #include #include #include #include "../libdebug/debug.h" #include "dsp.h" #include "sit.h" //#define DEBUG_DEMODULATOR #define NUM_TONES 8 #define db2level(db) pow(10, (double)(db) / 20.0) #define level2db(level) (20 * log10(level)) /* SS5 defines -9 dB (tx), -16 dB (min) for SF, R1 defines -8/-20 dB (tx), -27 dB (min) for SF */ static double tone_dbm[NUM_TONES] = { -7, -7, -7, -7, -7, -7, -9, -9 }; static double tone_freq[NUM_TONES] = { 700, 900, 1100, 1300, 1500, 1700, 2600, 2400 }; static double tone_width[NUM_TONES] = { 25, 25, 25, 25, 25, 25, 25, 25 }; static double tone_min_dbm[NUM_TONES] = { -14, -14, -14, -14, -14, -14, -16, -16 }; static double tone_min_ampl_sq[NUM_TONES]; static double tone_diff_db[NUM_TONES] = { 4, 4, 4, 4, 4, 4, 5, 5 }; static double tone_diff_ampl_sq[NUM_TONES]; void dsp_set_sf(double sf_db, double sf_min_db) { tone_dbm[6] = sf_db; tone_dbm[7] = sf_db; tone_min_dbm[6] = sf_min_db; tone_min_dbm[7] = sf_min_db; } int dsp_init_inst(dsp_t *dsp, void *priv, const char *name, double samplerate, int crosstalk, int comfort_noise, int delay_ms, double sense_db, double interrupt_recognition, double split_recognition, double mf_recognition, double kp_digit_duration, double other_digit_duration, double digit_pause, double pulse_pause, double notch, int ss5) { double tone_amplitude[NUM_TONES]; int t; int rc; PDEBUG(DDSP, DEBUG_DEBUG, "Init DSP instance.\n"); memset(dsp, 0, sizeof(*dsp)); dsp->priv = priv; strncpy(dsp->name, name, sizeof(dsp->name - 1)); dsp->samplerate = samplerate; dsp->crosstalk = crosstalk; dsp->comfort_noise = comfort_noise; // NO interrupt recognition, because the filter itself has a slow response // dsp->interrupt_recognition = (int)(1000.0 * interrupt_recognition); dsp->split_recognition = (int)(1000.0 * split_recognition); dsp->mf_recognition = (int)(1000.0 * mf_recognition); dsp->kp_digit_duration = kp_digit_duration; dsp->other_digit_duration = other_digit_duration; dsp->digit_pause = digit_pause; dsp->pulse_pause = pulse_pause; dsp->ms_per_sample = 1000.0 / samplerate; dsp->detect_tone = ' '; /* all levels are relative to 1mW */ for (t = 0; t < NUM_TONES; t++) { tone_amplitude[t] = db2level(tone_dbm[t]); tone_min_ampl_sq[t] = pow(db2level(tone_min_dbm[t] - sense_db), 2); tone_diff_ampl_sq[t] = pow(db2level(tone_diff_db[t]), 2); } /* init MF modulator */ dsp->mf_mod = mf_mod_init(samplerate, NUM_TONES, tone_freq, tone_amplitude); if (!dsp->mf_mod) return -EINVAL; /* init MF demodulator */ dsp->mf_demod = mf_demod_init(samplerate, NUM_TONES - (!ss5), tone_freq, tone_width); if (!dsp->mf_mod) return -EINVAL; /* alloc delay buffer */ if (delay_ms) { dsp->delay_length = (int)(dsp->samplerate * (double)delay_ms / 1000.0); dsp->delay_buffer = calloc(dsp->delay_length, sizeof(*dsp->delay_buffer)); } /* allocate jitter buffer */ rc = jitter_create(&dsp->tx_dejitter, "tx", 8000, sizeof(sample_t), JITTER_DATA); if (rc < 0) abort(); /* notch filter */ if (notch) { dsp->notch = 1; iir_notch_init(&dsp->notch_filter, notch, (int)dsp->samplerate, 1, 4); } return 0; } void dsp_cleanup_inst(dsp_t *dsp) { PDEBUG(DDSP, DEBUG_DEBUG, "Cleanup DSP instance.\n"); /* free FM modulator */ if (dsp->mf_mod) { mf_mod_exit(dsp->mf_mod); dsp->mf_mod = NULL; } /* free FM demodulator */ if (dsp->mf_demod) { mf_demod_exit(dsp->mf_demod); dsp->mf_demod = NULL; } /* free delay buffer */ if (dsp->delay_buffer) { free(dsp->delay_buffer); dsp->delay_buffer = NULL; } /* free jitter buffer */ jitter_destroy(&dsp->tx_dejitter); } /* * tone encoder */ static struct dsp_digits { char tone; uint32_t mask; } dsp_digits[] = { { '1', 0x01 + 0x02 }, { '2', 0x01 + 0x04 }, { '3', 0x02 + 0x04 }, { '4', 0x01 + 0x08 }, { '5', 0x02 + 0x08 }, { '6', 0x04 + 0x08 }, { '7', 0x01 + 0x10 }, { '8', 0x02 + 0x10 }, { '9', 0x04 + 0x10 }, { '0', 0x08 + 0x10 }, { '*', 0x01 + 0x20 }, /* code 11 */ { '#', 0x02 + 0x20 }, /* code 12 */ { 'a', 0x04 + 0x20 }, /* KP1 */ { 'b', 0x08 + 0x20 }, /* KP2 */ { 'c', 0x10 + 0x20 }, /* ST */ { 'B', 0x40 }, /* 2600 busy */ { 'A', 0x80 }, /* 2400 answer, acknowledge */ { 'C', 0x40 + 0x80 }, /* 2600+2400 clear forward */ { ' ', 0 }, /* silence */ { 0 , 0 }, }; /* set signaling tone duration threshold */ void set_sig_detect_duration(dsp_t *dsp, double duration_AB, double duration_C) { dsp->detect_count = 0; dsp->sig_detect_duration_AB = (int)(1000.0 * duration_AB); dsp->sig_detect_duration_C = (int)(1000.0 * duration_C); } /* set given tone with duration (ms) or continuously (0) */ int set_tone(dsp_t *dsp, char tone, double duration) { int i; dsp->tone = 0; if (!tone) { PDEBUG_CHAN(DDSP, DEBUG_DEBUG, "Remove tone\n"); return 0; } for (i = 0; dsp_digits[i].tone; i++) { if (dsp_digits[i].tone == tone) { dsp->tone_mask = dsp_digits[i].mask; dsp->tone = tone; dsp->tone_duration = (int)(dsp->samplerate * duration); if (duration) PDEBUG_CHAN(DDSP, DEBUG_DEBUG, "Set tone=\'%c\' duration=%.0fms (mask = 0x%02x)\n", dsp->tone, 1000.0 * duration, dsp->tone_mask); else PDEBUG_CHAN(DDSP, DEBUG_DEBUG, "Set tone=\'%c\' duration=continuous (mask = 0x%02x)\n", dsp->tone, dsp->tone_mask); return 0; } } PDEBUG_CHAN(DDSP, DEBUG_ERROR, "Tone '%c' does not exist.\n", tone); return -EINVAL; } void set_tone_transparent(dsp_t *dsp, int transparent) { PDEBUG_CHAN(DDSP, DEBUG_DEBUG, "Set tones %stransparent.\n", (!transparent) ? "non-" : ""); dsp->tone_transparent = transparent; } void set_sit(dsp_t *dsp, int on) { PDEBUG_CHAN(DDSP, DEBUG_DEBUG, "Turn sit %s.\n", (on) ? "on" : "off"); dsp->sit_on = on; dsp->sit_count = 0; } static int digit_to_pulses(char digit) { if (digit >= '1' && digit <= '9') return digit - '0'; if (digit == '0') return 10; if (digit == '*') return 11; if (digit == '#') return 12; return 0; } #define SF_PULSE_BREAK 0.060 #define SF_PULSE_MAKE 0.040 /* get next tone from dial string, if any */ static void get_tone_from_dial_string(dsp_t *dsp) { char tone; double duration; dsp->tone = 0; next_digit: if (dsp->dial_index == dsp->dial_length) { dsp->dial_length = 0; dialing_complete(dsp->priv); return; } /* get alternating tone/pause from dial string */ if (!dsp->digit_on) { /* start digit */ if (!dsp->pulsedialing) { /* MF digit on */ tone = dsp->dial_string[dsp->dial_index++]; if (tone == 'a' || tone == 'b') duration = dsp->kp_digit_duration; else duration = dsp->other_digit_duration; dsp->digit_on = 1; PDEBUG_CHAN(DDSP, DEBUG_DEBUG, "Send digit \'%c\' from dial string\n", tone); } else { /* SF pulse */ dsp->pulse_num = digit_to_pulses(dsp->dial_string[dsp->dial_index++]); if (dsp->pulse_num == 0) { PDEBUG_CHAN(DDSP, DEBUG_DEBUG, "Skipping digit \'%c\' from dial string that cannot be puled\n", dsp->dial_string[dsp->dial_index]); goto next_digit; } /* pulse on */ tone = 'B'; dsp->pulse_on = 1; duration = SF_PULSE_BREAK; dsp->digit_on = 1; PDEBUG_CHAN(DDSP, DEBUG_DEBUG, "Send digit \'%c\' from dial string as pulses\n", dsp->dial_string[dsp->dial_index]); } } else { /* ongoing digit */ if (dsp->pulse_num) { if (dsp->pulse_on) { /* pulse off */ tone = ' '; dsp->pulse_on = 0; duration = SF_PULSE_MAKE; if (++dsp->pulse_count == dsp->pulse_num) { dsp->pulse_count = 0; dsp->digit_on = 0; duration = dsp->pulse_pause; PDEBUG_CHAN(DDSP, DEBUG_DEBUG, "Send pause after pulsing digits from dial string\n"); } } else { /* pulse on */ tone = 'B'; dsp->pulse_on = 1; duration = SF_PULSE_BREAK; } } else { /* digit pause */ tone = ' '; dsp->digit_on = 0; duration = dsp->digit_pause; PDEBUG_CHAN(DDSP, DEBUG_DEBUG, "Send pause after digit from dial string\n"); } } set_tone(dsp, tone, duration); } /* set given dial string */ void set_dial_string(dsp_t *dsp, const char *dial, int pulsedialing) { dsp->digit_on = 0; strncpy(dsp->dial_string, dial, sizeof(dsp->dial_string) - 1); dsp->dial_index = 0; dsp->dial_length = strlen(dsp->dial_string); dsp->pulse_on = 0; dsp->pulse_count = 0; dsp->pulsedialing = pulsedialing; } /* determine which tones to be modulated, get next tone, if elapsed */ static int assemble_tones(dsp_t *dsp, uint32_t *mask, int length) { int i; for (i = 0; i < length; i++) { /* if tone was done, try to get next digit */ if (!dsp->tone) { if (!dsp->dial_length) return i; get_tone_from_dial_string(dsp); if (!dsp->tone) return i; } *mask++ = dsp->tone_mask; if (dsp->tone_duration) { /* count down duration, if tones is not continuous */ if (!(--dsp->tone_duration)) dsp->tone = 0; } } return i; } /* * tone deencoder */ /* detection array for one frequency */ static char decode_one[8] = { ' ', ' ', ' ', ' ', ' ', ' ', 'B', 'A' }; /* A = 2400, B = 2600 */ /* detection matrix for two frequencies */ static char decode_two[8][8] = { { ' ', '1', '2', '4', '7', '*', ' ', ' ' }, /* * = code 11 */ { '1', ' ', '3', '5', '8', '#', ' ', ' ' }, /* # = code 12 */ { '2', '3', ' ', '6', '9', 'a', ' ', ' ' }, /* a = KP1 */ { '4', '5', '6', ' ', '0', 'b', ' ', ' ' }, /* b = KP2 */ { '7', '8', '9', '0', ' ', 'c', ' ', ' ' }, /* c = ST */ { '*', '#', 'a', 'b', 'c', ' ', ' ', ' ' }, { ' ', ' ', ' ', ' ', ' ', ' ', ' ', 'C' }, /* C = 2600+2400 */ { ' ', ' ', ' ', ' ', ' ', ' ', 'C', ' ' } }; /* determine which tone is played */ static void detect_tones(dsp_t *dsp, sample_t *samples, sample_t **levels_squared, int length, int incoming) { int f1, f2; double f1_level_squared, f2_level_squared; char tone; int s, t, l; for (s = 0; s < length; s++) { /* only perform tone detection/muting every millisecond */ dsp->ms_interval += dsp->ms_per_sample; dsp->ms_count++; if (dsp->ms_interval < 1.0) continue; /* mute/notch if split duration reached (this is the chunk, see below for the rest) */ if (dsp->split_recognition && dsp->split_count == dsp->split_recognition) { /* l is the number of samples required to mute for this interval of one milliseconds */ l = (dsp->ms_count < s + 1) ? dsp->ms_count : s + 1; if (dsp->notch) iir_process(&dsp->notch_filter, samples + s + 1 - l, l); else memset(samples + s + 1 - l, 0, l * sizeof(*samples)); } dsp->ms_interval -= 1.0; dsp->ms_count = 0; #ifdef DEBUG_DEMODULATOR if (incoming) { for (t = 0; t < dsp->mf_demod->tones; t++) { char level[20]; int db; memset(level, 32, sizeof(level)); db = roundf(level2db(sqrt(levels_squared[t][s])) + 25); if (db >= 0 && db < (int)sizeof(level)) level[db] = '*'; level[sizeof(level)-1]=0; printf("%s|", level); } printf("\n"); } #endif /* find the tone which is the loudest */ f1 = -1; f1_level_squared = -1.0; for (t = 0; t < dsp->mf_demod->tones; t++) { if (levels_squared[t][s] > f1_level_squared) { f1_level_squared = levels_squared[t][s]; f1 = t; } } /* find the tone which is the second loudest */ f2 = -1; f2_level_squared = -1.0; for (t = 0; t < dsp->mf_demod->tones; t++) { if (t == f1) continue; if (levels_squared[t][s] > f2_level_squared) { f2_level_squared = levels_squared[t][s]; f2 = t; } } /* now check if the minimum level is reached */ if (f1 >= 0 && f1_level_squared < tone_min_ampl_sq[f1]) f1 = -1; if (f2 >= 0 && f2_level_squared < tone_min_ampl_sq[f2]) f2 = -1; #ifdef DEBUG_DEMODULATOR if (incoming) printf("%s f1=%.0f (%.1f dBm) f2=%.0f (%.1f dBm)\n", CHAN, (f1 >= 0) ? tone_freq[f1] : 0, level2db(sqrt(f1_level_squared)), (f2 >= 0) ? tone_freq[f2] : 0, level2db(sqrt(f2_level_squared))); #endif /* check if no, one or two tones are detected */ if (f1 < 0) tone = ' '; else if (f2 < 0) tone = decode_one[f1]; else { if (f2_level_squared * tone_diff_ampl_sq[f2] < f1_level_squared) tone = ' '; else tone = decode_two[f1][f2]; } /* process interrupt counting, keep tone until interrupt counter expires */ if (dsp->interrupt_recognition) { if (dsp->detect_tone != ' ' && tone != dsp->detect_tone) { #ifdef DEBUG_DEMODULATOR if (!dsp->interrupt_count) if (incoming) PDEBUG_CHAN(DDSP, DEBUG_DEBUG, "interruption detected s=%d old='%c' new='%c'\n", s, dsp->detect_tone, tone); #endif if (dsp->interrupt_count < dsp->interrupt_recognition) { dsp->interrupt_count++; tone = dsp->detect_tone; } } else { #ifdef DEBUG_DEMODULATOR if (dsp->interrupt_count) if (incoming) PDEBUG_CHAN(DDSP, DEBUG_DEBUG, "interruption ceased at count %d s=%d old='%c' new='%c'\n", dsp->interrupt_count, s, dsp->detect_tone, tone); #endif dsp->interrupt_count = 0; } } /* split audio, after minimum duration of detecting a tone */ if (tone >= 'A' && tone <= 'C') { if (dsp->split_count < dsp->split_recognition) dsp->split_count++; } else dsp->split_count = 0; /* some change in tone */ if (dsp->detect_tone != tone) { if (dsp->detect_count == 0) PDEBUG_CHAN(DDSP, DEBUG_DEBUG, "Detected new tone '%c' (%.1f dBm)\n", tone, level2db(sqrt(f1_level_squared))); switch (tone) { case 'A': case 'B': /* tone appears, wait some time */ if (dsp->detect_count < dsp->sig_detect_duration_AB) dsp->detect_count++; else { #ifdef DEBUG_DEMODULATOR if (incoming) PDEBUG_CHAN(DDSP, DEBUG_DEBUG, "Tone stable '%c' (%.1f dBm)\n", tone, level2db(sqrt(f1_level_squared))); #endif /* sign tone detected */ dsp->detect_count = 0; dsp->detect_tone = tone; receive_signal(dsp->priv, tone, level2db(sqrt(f1_level_squared))); } break; case 'C': /* tone appears, wait some time */ if (dsp->detect_count < dsp->sig_detect_duration_C) dsp->detect_count++; else { /* sign tone detected */ dsp->detect_count = 0; dsp->detect_tone = tone; receive_signal(dsp->priv, tone, level2db(sqrt(f1_level_squared))); } break; case ' ': /* tone ceases, there is no counting here, since interrupt counting does the job for us */ dsp->detect_count = 0; dsp->detect_tone = tone; if (incoming) PDEBUG_CHAN(DDSP, DEBUG_DEBUG, "cease\n"); receive_signal(dsp->priv, tone, 0.0); break; default: /* tone appears, wait some time */ if (dsp->detect_count < dsp->mf_recognition) dsp->detect_count++; else { /* sign tone detected */ dsp->detect_count = 0; dsp->detect_tone = tone; receive_signal(dsp->priv, tone, level2db(sqrt(f1_level_squared))); } } } else dsp->detect_count = 0; } /* mute/notch if split duration reached (this is the rest, see above for each chunk) */ if (dsp->split_recognition && dsp->split_count == dsp->split_recognition) { /* l is the number of samples that are left */ l = (dsp->ms_count < s) ? dsp->ms_count : s; if (dsp->notch) iir_process(&dsp->notch_filter, samples + s - l, l); else memset(samples + s - l, 0, l * sizeof(*samples)); } } /* process audio from one link (source) to another (destination) */ static void process_audio(dsp_t *dsp_a, dsp_t *dsp_b, int length) { sample_t samples[2][length], s; sample_t b1[length], b2[length], b3[length], b4[length], b5[length], b6[length], b7[length], b8[length]; sample_t *levels_squared[NUM_TONES] = { b1, b2, b3, b4, b5, b6, b7, b8 }; uint32_t mask[length]; int16_t data[160]; int count1, count2; int i; /* get audio from jitter buffer */ jitter_load(&dsp_a->tx_dejitter, samples[0], length); jitter_load(&dsp_b->tx_dejitter, samples[1], length); /* optionally add (-36 dBm) comfort noise */ if (!dsp_a->cc_callref && dsp_a->comfort_noise) { for (i = 0; i < length; i++) samples[0][i] += (double)((int8_t)random()) / 16384.0; } if (!dsp_b->cc_callref && dsp_b->comfort_noise) { for (i = 0; i < length; i++) samples[1][i] += (double)((int8_t)random()) / 16385.0; } /* send SIT, if on */ if (dsp_a->sit_on) dsp_a->sit_count = sit_play(samples[0], dsp_a->sit_count, length); if (dsp_b->sit_on) dsp_b->sit_count = sit_play(samples[1], dsp_b->sit_count, length); /* modulate tone/digit. if no tone has to be played (or it stopped), count is less than length */ count1 = assemble_tones(dsp_a, mask, length); mf_mod(dsp_a->mf_mod, mask, samples[0], count1, dsp_a->tone_transparent); count2 = assemble_tones(dsp_b, mask, length); mf_mod(dsp_b->mf_mod, mask, samples[1], count2, dsp_b->tone_transparent); /* ! here is the bridge from a to b and from b to a ! */ /* optionally add one way delay */ if (dsp_b->delay_buffer) { for (i = 0; i < length; i++) { s = dsp_b->delay_buffer[dsp_b->delay_index]; dsp_b->delay_buffer[dsp_b->delay_index] = samples[0][i]; if (++(dsp_b->delay_index) == dsp_b->delay_length) dsp_b->delay_index = 0; samples[0][i] = s; } } if (dsp_a->delay_buffer) { for (i = 0; i < length; i++) { s = dsp_a->delay_buffer[dsp_a->delay_index]; dsp_a->delay_buffer[dsp_a->delay_index] = samples[1][i]; if (++(dsp_a->delay_index) == dsp_a->delay_length) dsp_a->delay_index = 0; samples[1][i] = s; } } /* demodulate and call tone detector */ mf_demod(dsp_b->mf_demod, samples[0], length, levels_squared); detect_tones(dsp_b, samples[0], levels_squared, length, 1); mf_demod(dsp_a->mf_demod, samples[1], length, levels_squared); detect_tones(dsp_a, samples[1], levels_squared, length, 0); /* optionally add some crosstalk */ if (dsp_a->crosstalk) { /* use count, since it carries number of samples with signalling */ for (i = 0; i < count1; i++) samples[1][i] += samples[0][i] / 70.0; } if (dsp_b->crosstalk) { /* use count, since it carries number of samples with signalling */ for (i = 0; i < count2; i++) samples[0][i] += samples[1][i] / 70.0; } /* forward audio to CC if call exists */ if (dsp_b->cc_callref && dsp_b->codec) { samples_to_int16_1mw(data, samples[0], length); osmo_cc_rtp_send(dsp_b->codec, (uint8_t *)data, length * 2, 0, 1, length, dsp_b); } if (dsp_a->cc_callref && dsp_a->codec) { samples_to_int16_1mw(data, samples[1], length); osmo_cc_rtp_send(dsp_a->codec, (uint8_t *)data, length * 2, 0, 1, length, dsp_a); } } /* clock is called every given number of samples (20ms) */ void audio_clock(dsp_t *sunset_list, dsp_t *sunrise_list, int len) { dsp_t *dsp_a, *dsp_b; if (!sunset_list) return; if (!sunrise_list) { /* each pair of links on the same endpoint are bridged */ for (dsp_b = sunset_list; dsp_b; dsp_b = dsp_b->next) { dsp_a = dsp_b; dsp_b = dsp_b->next; if (!dsp_b) break; process_audio(dsp_a, dsp_b, len); } } else { /* each link on two endpoints are bridged */ for (dsp_a = sunset_list, dsp_b = sunrise_list; dsp_a && dsp_b; dsp_a = dsp_a->next, dsp_b = dsp_b->next) { process_audio(dsp_a, dsp_b, len); } } } /* take audio from CC and store in jitter buffer */ void down_audio(struct osmo_cc_session_codec *codec, uint8_t __attribute__((unused)) marker, uint16_t sequence, uint32_t timestamp, uint32_t ssrc, uint8_t *data, int len) { dsp_t *dsp = codec->media->session->priv; int count = len/2; sample_t samples[count]; int16_to_samples_1mw(samples, (int16_t *)data, count); jitter_save(&dsp->tx_dejitter, samples, count, 1, sequence, timestamp, ssrc); } void encode_l16(uint8_t *src_data, int src_len, uint8_t **dst_data, int *dst_len, void __attribute__((unused)) *priv) { uint16_t *src = (uint16_t *)src_data, *dst; int len = src_len / 2, i; dst = malloc(len * 2); if (!dst) return; for (i = 0; i < len; i++) dst[i] = htons(src[i]); *dst_data = (uint8_t *)dst; *dst_len = len * 2; } void decode_l16(uint8_t *src_data, int src_len, uint8_t **dst_data, int *dst_len, void __attribute__((unused)) *priv) { uint16_t *src = (uint16_t *)src_data, *dst; int len = src_len / 2, i; dst = malloc(len * 2); if (!dst) return; for (i = 0; i < len; i++) dst[i] = ntohs(src[i]); *dst_data = (uint8_t *)dst; *dst_len = len * 2; }