Move more common code; make it usable for SS5 and R1
This commit is contained in:
parent
91e8521071
commit
2483f1c2b1
|
@ -3,6 +3,9 @@ AM_CPPFLAGS = -Wall -Wextra -g $(all_includes)
|
||||||
noinst_LIBRARIES = libcommon.a
|
noinst_LIBRARIES = libcommon.a
|
||||||
|
|
||||||
libcommon_a_SOURCES = \
|
libcommon_a_SOURCES = \
|
||||||
|
common.c \
|
||||||
mf.c \
|
mf.c \
|
||||||
dsp.c
|
dsp.c \
|
||||||
|
sit.c \
|
||||||
|
display_status.c
|
||||||
|
|
||||||
|
|
|
@ -0,0 +1,345 @@
|
||||||
|
/* common process
|
||||||
|
*
|
||||||
|
* (C) 2020 by Andreas Eversberg <jolly@eversberg.eu>
|
||||||
|
* 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 <http://www.gnu.org/licenses/>.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include <stdio.h>
|
||||||
|
#include <string.h>
|
||||||
|
#include <unistd.h>
|
||||||
|
#include <stdlib.h>
|
||||||
|
#include <stdint.h>
|
||||||
|
#include <errno.h>
|
||||||
|
#include <sys/types.h>
|
||||||
|
#include "../libdebug/debug.h"
|
||||||
|
#include "../libtimer/timer.h"
|
||||||
|
#include "../libselect/select.h"
|
||||||
|
#include "../libosmocc/endpoint.h"
|
||||||
|
#include "../libosmocc/helper.h"
|
||||||
|
#include "common.h"
|
||||||
|
|
||||||
|
/*
|
||||||
|
* generate messages towards CC
|
||||||
|
*/
|
||||||
|
|
||||||
|
void reject_call(osmo_cc_endpoint_t *cc_ep, uint32_t callref, uint8_t isdn_cause)
|
||||||
|
|
||||||
|
{
|
||||||
|
osmo_cc_msg_t *new_msg;
|
||||||
|
|
||||||
|
/* create osmo-cc message */
|
||||||
|
new_msg = osmo_cc_new_msg(OSMO_CC_MSG_REJ_IND);
|
||||||
|
/* cause */
|
||||||
|
osmo_cc_add_ie_cause(new_msg, OSMO_CC_LOCATION_BEYOND_INTERWORKING, isdn_cause, 0, 0);
|
||||||
|
/* send message to osmo-cc */
|
||||||
|
osmo_cc_ll_msg(cc_ep, callref, new_msg);
|
||||||
|
}
|
||||||
|
|
||||||
|
void release_call(osmo_cc_endpoint_t *cc_ep, uint32_t callref, uint8_t isdn_cause)
|
||||||
|
{
|
||||||
|
osmo_cc_msg_t *new_msg;
|
||||||
|
|
||||||
|
/* create osmo-cc message */
|
||||||
|
new_msg = osmo_cc_new_msg(OSMO_CC_MSG_REL_IND);
|
||||||
|
/* cause */
|
||||||
|
osmo_cc_add_ie_cause(new_msg, OSMO_CC_LOCATION_BEYOND_INTERWORKING, isdn_cause, 0, 0);
|
||||||
|
/* send message to osmo-cc */
|
||||||
|
osmo_cc_ll_msg(cc_ep, callref, new_msg);
|
||||||
|
}
|
||||||
|
|
||||||
|
void disconnect_call(osmo_cc_endpoint_t *cc_ep, uint32_t callref, uint8_t isdn_cause)
|
||||||
|
{
|
||||||
|
osmo_cc_msg_t *new_msg;
|
||||||
|
|
||||||
|
/* create osmo-cc message */
|
||||||
|
new_msg = osmo_cc_new_msg(OSMO_CC_MSG_DISC_IND);
|
||||||
|
/* progress */
|
||||||
|
osmo_cc_add_ie_progress(new_msg, OSMO_CC_CODING_ITU_T, OSMO_CC_LOCATION_BEYOND_INTERWORKING, OSMO_CC_PROGRESS_INBAND_INFO_AVAILABLE);
|
||||||
|
/* cause */
|
||||||
|
osmo_cc_add_ie_cause(new_msg, OSMO_CC_LOCATION_BEYOND_INTERWORKING, isdn_cause, 0, 0);
|
||||||
|
/* send message to osmo-cc */
|
||||||
|
osmo_cc_ll_msg(cc_ep, callref, new_msg);
|
||||||
|
}
|
||||||
|
|
||||||
|
void proceed_call(osmo_cc_endpoint_t *cc_ep, uint32_t callref, const char *sdp)
|
||||||
|
{
|
||||||
|
osmo_cc_msg_t *new_msg;
|
||||||
|
|
||||||
|
/* create osmo-cc message */
|
||||||
|
new_msg = osmo_cc_new_msg(OSMO_CC_MSG_PROC_IND);
|
||||||
|
/* progress */
|
||||||
|
osmo_cc_add_ie_progress(new_msg, OSMO_CC_CODING_ITU_T, OSMO_CC_LOCATION_BEYOND_INTERWORKING, OSMO_CC_PROGRESS_INBAND_INFO_AVAILABLE);
|
||||||
|
/* sdp */
|
||||||
|
osmo_cc_add_ie_sdp(new_msg, sdp);
|
||||||
|
/* send message to osmo-cc */
|
||||||
|
osmo_cc_ll_msg(cc_ep, callref, new_msg);
|
||||||
|
}
|
||||||
|
|
||||||
|
void alert_call(osmo_cc_endpoint_t *cc_ep, uint32_t callref)
|
||||||
|
{
|
||||||
|
osmo_cc_msg_t *new_msg;
|
||||||
|
|
||||||
|
/* create osmo-cc message */
|
||||||
|
new_msg = osmo_cc_new_msg(OSMO_CC_MSG_ALERT_IND);
|
||||||
|
/* send message to osmo-cc */
|
||||||
|
osmo_cc_ll_msg(cc_ep, callref, new_msg);
|
||||||
|
}
|
||||||
|
|
||||||
|
void answer_call(osmo_cc_endpoint_t *cc_ep, uint32_t callref)
|
||||||
|
{
|
||||||
|
osmo_cc_msg_t *new_msg;
|
||||||
|
|
||||||
|
/* create osmo-cc message */
|
||||||
|
new_msg = osmo_cc_new_msg(OSMO_CC_MSG_SETUP_CNF);
|
||||||
|
/* send message to osmo-cc */
|
||||||
|
osmo_cc_ll_msg(cc_ep, callref, new_msg);
|
||||||
|
}
|
||||||
|
|
||||||
|
void setup_comp_call(osmo_cc_endpoint_t *cc_ep, uint32_t callref)
|
||||||
|
{
|
||||||
|
osmo_cc_msg_t *new_msg;
|
||||||
|
|
||||||
|
/* create osmo-cc message */
|
||||||
|
new_msg = osmo_cc_new_msg(OSMO_CC_MSG_SETUP_COMP_IND);
|
||||||
|
/* send message to osmo-cc */
|
||||||
|
osmo_cc_ll_msg(cc_ep, callref, new_msg);
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* dial string generation and parsing
|
||||||
|
*/
|
||||||
|
|
||||||
|
static char *prefix_1_digit[] = { "1", "7", NULL };
|
||||||
|
static char *prefix_2_digit[] = {
|
||||||
|
"20", "27", "28", "30", "31", "32", "33", "34", "36", "39", "40", "41",
|
||||||
|
"43", "44", "45", "46", "47", "48", "49", "51", "52", "53", "54", "55",
|
||||||
|
"56", "57", "58", "60", "61", "62", "63", "64", "65", "66", "81", "82",
|
||||||
|
"83", "84", "86", "89", "90", "91", "92", "93", "94", "95", "98",
|
||||||
|
NULL };
|
||||||
|
|
||||||
|
/* use number and number type to generate an SS5 dial string
|
||||||
|
* the digits are checked if they can be dialed
|
||||||
|
* if the number is already in SS5 format, only digits are checked
|
||||||
|
*/
|
||||||
|
int generate_dial_string(uint8_t type, const char *dialing, char *string, int string_size)
|
||||||
|
{
|
||||||
|
int full_string_given = 0;
|
||||||
|
int i, ii;
|
||||||
|
|
||||||
|
if ((int)strlen(dialing) + 4 > string_size) {
|
||||||
|
PDEBUG(DSS5, DEBUG_NOTICE, "Dial string is too long for our digit register, call is rejected!\n");
|
||||||
|
return -EINVAL;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* check for correct digits */
|
||||||
|
for (i = 0, ii = strlen(dialing); i < ii; i++) {
|
||||||
|
/* string may start with 'a' or 'b', but then 'c' must be the last digit */
|
||||||
|
if (dialing[i] == 'a' || dialing[i] == 'b') {
|
||||||
|
full_string_given = 1;
|
||||||
|
if (dialing[ii - 1] != 'c') {
|
||||||
|
PDEBUG(DSS5, DEBUG_NOTICE, "Number starts with 'a' (KP1) or 'b' (KP2) but missing 'c' (ST) at the end, call is rejected!\n");
|
||||||
|
return -EINVAL;
|
||||||
|
}
|
||||||
|
/* remove check of last digit 'c' */
|
||||||
|
--ii;
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
/* string must only consist of numerical digits and '*' (code 11) and '#' (code 12) */
|
||||||
|
if (!strchr("0123456789*#", dialing[i])) {
|
||||||
|
PDEBUG(DSS5, DEBUG_NOTICE, "Number has invalid digits, call is rejected!\n");
|
||||||
|
return -EINVAL;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/* if full string with 'a'/'b' and 'c' is given, we have complete dial string */
|
||||||
|
if (full_string_given) {
|
||||||
|
strcpy(string, dialing);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* if number is not of international type, create national dial string */
|
||||||
|
if (type != OSMO_CC_TYPE_INTERNATIONAL) {
|
||||||
|
// make GCC happy
|
||||||
|
strcpy(string, "a0");
|
||||||
|
strcat(string, dialing);
|
||||||
|
strcat(string, "c");
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* check international prefix with length of 1 digit */
|
||||||
|
if ((int)strlen(dialing) < 1) {
|
||||||
|
PDEBUG(DSS5, DEBUG_NOTICE, "International number too short to get country code from, call is rejected!\n");
|
||||||
|
return -EINVAL;
|
||||||
|
}
|
||||||
|
for (i = 0; prefix_1_digit[i]; i++) {
|
||||||
|
if (prefix_1_digit[i][0] == dialing[0])
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
/* if number is of international type, create international dial string */
|
||||||
|
if (prefix_1_digit[i]) {
|
||||||
|
#pragma GCC diagnostic push
|
||||||
|
#pragma GCC diagnostic ignored "-Wformat-overflow"
|
||||||
|
sprintf(string, "b%c0%sc", dialing[0], dialing + 1);
|
||||||
|
#pragma GCC diagnostic pop
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* check international prefix with length of 2 digits */
|
||||||
|
if ((int)strlen(dialing) < 2) {
|
||||||
|
PDEBUG(DSS5, DEBUG_NOTICE, "International number too short to get country code from, call is rejected!\n");
|
||||||
|
return -EINVAL;
|
||||||
|
}
|
||||||
|
for (i = 0; prefix_2_digit[i]; i++) {
|
||||||
|
if (prefix_2_digit[i][0] == dialing[0]
|
||||||
|
&& prefix_2_digit[i][1] == dialing[1])
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
/* if number is of international type, create international dial string */
|
||||||
|
if (prefix_2_digit[i]) {
|
||||||
|
#pragma GCC diagnostic push
|
||||||
|
#pragma GCC diagnostic ignored "-Wformat-overflow"
|
||||||
|
sprintf(string, "b%c%c0%sc", dialing[0], dialing[1], dialing + 2);
|
||||||
|
#pragma GCC diagnostic pop
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* check international prefix with length of 3 digits */
|
||||||
|
if ((int)strlen(dialing) < 3) {
|
||||||
|
PDEBUG(DSS5, DEBUG_NOTICE, "International number too short to get country code from, call is rejected!\n");
|
||||||
|
return -EINVAL;
|
||||||
|
}
|
||||||
|
/* if number is of international type, create international dial string */
|
||||||
|
#pragma GCC diagnostic push
|
||||||
|
#pragma GCC diagnostic ignored "-Wformat-overflow"
|
||||||
|
sprintf(string, "b%c%c%c0%sc", dialing[0], dialing[1], dialing[2], dialing + 3);
|
||||||
|
#pragma GCC diagnostic pop
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* parse received SS5 dial string and convert it into a national or international number */
|
||||||
|
int parse_dial_string(uint8_t *type, char *dialing, int dialing_size, const char *string)
|
||||||
|
{
|
||||||
|
char kp_digit;
|
||||||
|
const char *prefix;
|
||||||
|
int length;
|
||||||
|
int i;
|
||||||
|
|
||||||
|
/* remove start and stop digits, set string after start digit and set length to digits between start and stop */
|
||||||
|
if (string[0] != 'a' && string[0] != 'b') {
|
||||||
|
PDEBUG(DSS5, DEBUG_NOTICE, "Received digits do not start with 'a' (KP1) nor 'b' (KP2), call is rejected!\n");
|
||||||
|
return -EINVAL;
|
||||||
|
}
|
||||||
|
length = strlen(string) - 1;
|
||||||
|
if (string[length] != 'c') {
|
||||||
|
PDEBUG(DSS5, DEBUG_NOTICE, "Received digits do end with 'c' (ST), call is rejected!\n");
|
||||||
|
return -EINVAL;
|
||||||
|
}
|
||||||
|
kp_digit = *string++;
|
||||||
|
length--;
|
||||||
|
if (length > dialing_size - 1) {
|
||||||
|
PDEBUG(DSS5, DEBUG_NOTICE, "Received dial string is too long, call is rejected!\n");
|
||||||
|
return -EINVAL;
|
||||||
|
}
|
||||||
|
if (!length) {
|
||||||
|
PDEBUG(DSS5, DEBUG_NOTICE, "Nothing dialed between KP and ST, call is rejected!\n");
|
||||||
|
return -EINVAL;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* received local or national call */
|
||||||
|
if (kp_digit == 'a') {
|
||||||
|
/* remove discriminaing digit */
|
||||||
|
if (string[0] == '0') {
|
||||||
|
string++;
|
||||||
|
length--;
|
||||||
|
*type = OSMO_CC_TYPE_NATIONAL;
|
||||||
|
} else
|
||||||
|
*type = OSMO_CC_TYPE_UNKNOWN;
|
||||||
|
strncpy(dialing, string, length);
|
||||||
|
dialing[length] = '\0';
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* check international prefix with length of 1 digit */
|
||||||
|
if (length < 2) {
|
||||||
|
PDEBUG(DSS5, DEBUG_NOTICE, "International number too short to get country code from, call is rejected!\n");
|
||||||
|
return -EINVAL;
|
||||||
|
}
|
||||||
|
for (i = 0; prefix_1_digit[i]; i++) {
|
||||||
|
if (prefix_1_digit[i][0] == string[0])
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
/* if number is of international type, create international dial string */
|
||||||
|
if (prefix_1_digit[i]) {
|
||||||
|
prefix = string;
|
||||||
|
string += 1;
|
||||||
|
length -= 1;
|
||||||
|
/* remove discriminaing digit */
|
||||||
|
string++;
|
||||||
|
--length;
|
||||||
|
*type = OSMO_CC_TYPE_INTERNATIONAL;
|
||||||
|
dialing[0] = prefix[0];
|
||||||
|
strncpy(dialing + 1, string, length);
|
||||||
|
dialing[1 + length] = '\0';
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* check international prefix with length of 2 digits */
|
||||||
|
if (length < 3) {
|
||||||
|
PDEBUG(DSS5, DEBUG_NOTICE, "International number too short to get country code from, call is rejected!\n");
|
||||||
|
return -EINVAL;
|
||||||
|
}
|
||||||
|
for (i = 0; prefix_2_digit[i]; i++) {
|
||||||
|
if (prefix_2_digit[i][0] == string[0]
|
||||||
|
&& prefix_2_digit[i][1] == string[1])
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
/* if number is of international type, create international dial string */
|
||||||
|
if (prefix_2_digit[i]) {
|
||||||
|
prefix = string;
|
||||||
|
string += 2;
|
||||||
|
length -= 2;
|
||||||
|
/* remove discriminaing digit */
|
||||||
|
string++;
|
||||||
|
--length;
|
||||||
|
*type = OSMO_CC_TYPE_INTERNATIONAL;
|
||||||
|
dialing[0] = prefix[0];
|
||||||
|
dialing[1] = prefix[1];
|
||||||
|
strncpy(dialing + 2, string, length);
|
||||||
|
dialing[2 + length] = '\0';
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* check international prefix with length of 3 digits */
|
||||||
|
if (length < 4) {
|
||||||
|
PDEBUG(DSS5, DEBUG_NOTICE, "International number too short to get country code from, call is rejected!\n");
|
||||||
|
return -EINVAL;
|
||||||
|
}
|
||||||
|
/* if number is of international type, create international dial string */
|
||||||
|
prefix = string;
|
||||||
|
string += 3;
|
||||||
|
length -= 3;
|
||||||
|
/* remove discriminaing digit */
|
||||||
|
string++;
|
||||||
|
--length;
|
||||||
|
*type = OSMO_CC_TYPE_INTERNATIONAL;
|
||||||
|
dialing[0] = prefix[0];
|
||||||
|
dialing[1] = prefix[1];
|
||||||
|
dialing[2] = prefix[2];
|
||||||
|
strncpy(dialing + 3, string, length);
|
||||||
|
dialing[3 + length] = '\0';
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
|
@ -0,0 +1,11 @@
|
||||||
|
|
||||||
|
void reject_call(osmo_cc_endpoint_t *cc_ep, uint32_t callref, uint8_t isdn_cause);
|
||||||
|
void release_call(osmo_cc_endpoint_t *cc_ep, uint32_t callref, uint8_t isdn_cause);
|
||||||
|
void disconnect_call(osmo_cc_endpoint_t *cc_ep, uint32_t callref, uint8_t isdn_cause);
|
||||||
|
void proceed_call(osmo_cc_endpoint_t *cc_ep, uint32_t callref, const char *sdp);
|
||||||
|
void alert_call(osmo_cc_endpoint_t *cc_ep, uint32_t callref);
|
||||||
|
void answer_call(osmo_cc_endpoint_t *cc_ep, uint32_t callref);
|
||||||
|
void setup_comp_call(osmo_cc_endpoint_t *cc_ep, uint32_t callref);
|
||||||
|
int generate_dial_string(uint8_t type, const char *dialing, char *string, int string_size);
|
||||||
|
int parse_dial_string(uint8_t *type, char *dialing, int dialing_size, const char *string);
|
||||||
|
|
|
@ -4,6 +4,6 @@
|
||||||
|
|
||||||
void display_status_on(int on);
|
void display_status_on(int on);
|
||||||
void display_status_start(void);
|
void display_status_start(void);
|
||||||
void display_status_line(const char *if_name, int link_count, const char *from_id, const char *to_id, enum ss5_state state);
|
void display_status_line(const char *if_name, int link_count, const char *from_id, const char *to_id, const char *state_name);
|
||||||
void display_status_end(void);
|
void display_status_end(void);
|
||||||
|
|
|
@ -22,7 +22,6 @@
|
||||||
#include <string.h>
|
#include <string.h>
|
||||||
#include <sys/types.h>
|
#include <sys/types.h>
|
||||||
#include "../libdebug/debug.h"
|
#include "../libdebug/debug.h"
|
||||||
#include "ss5.h"
|
|
||||||
#include "display.h"
|
#include "display.h"
|
||||||
|
|
||||||
static int status_on = 0;
|
static int status_on = 0;
|
||||||
|
@ -97,7 +96,7 @@ void display_status_start(void)
|
||||||
line_count = 1;
|
line_count = 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
void display_status_line(const char *if_name, int link_count, const char *from_id, const char *to_id, enum ss5_state state)
|
void display_status_line(const char *if_name, int link_count, const char *from_id, const char *to_id, const char *state_name)
|
||||||
{
|
{
|
||||||
char line[MAX_DISPLAY_WIDTH + 4096];
|
char line[MAX_DISPLAY_WIDTH + 4096];
|
||||||
char color[MAX_DISPLAY_WIDTH + 4096];
|
char color[MAX_DISPLAY_WIDTH + 4096];
|
||||||
|
@ -136,8 +135,8 @@ void display_status_line(const char *if_name, int link_count, const char *from_i
|
||||||
index += 4 + from_len + 2 + 4 + to_len;
|
index += 4 + from_len + 2 + 4 + to_len;
|
||||||
line[index] = '\0';
|
line[index] = '\0';
|
||||||
|
|
||||||
strcpy(line + index, ss5_state_names[state]);
|
strcpy(line + index, state_name);
|
||||||
memset(color + index, 7, strlen(ss5_state_names[state]));
|
memset(color + index, 7, strlen(state_name));
|
||||||
}
|
}
|
||||||
|
|
||||||
/* store line without CR, but not more than MAX_DISPLAY_WIDTH - 1 */
|
/* store line without CR, but not more than MAX_DISPLAY_WIDTH - 1 */
|
274
src/common/dsp.c
274
src/common/dsp.c
|
@ -30,6 +30,7 @@
|
||||||
#include <arpa/inet.h>
|
#include <arpa/inet.h>
|
||||||
#include "../libdebug/debug.h"
|
#include "../libdebug/debug.h"
|
||||||
#include "dsp.h"
|
#include "dsp.h"
|
||||||
|
#include "sit.h"
|
||||||
|
|
||||||
//#define DEBUG_DEMODULATOR
|
//#define DEBUG_DEMODULATOR
|
||||||
|
|
||||||
|
@ -37,8 +38,9 @@
|
||||||
#define db2level(db) pow(10, (double)(db) / 20.0)
|
#define db2level(db) pow(10, (double)(db) / 20.0)
|
||||||
#define level2db(level) (20 * log10(level))
|
#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_dbm[NUM_TONES] = { -7, -7, -7, -7, -7, -7, -9, -9 };
|
||||||
static double tone_freq[NUM_TONES] = { 700, 900, 1100, 1300, 1500, 1700, 2400, 2600 };
|
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_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_dbm[NUM_TONES] = { -14, -14, -14, -14, -14, -14, -16, -16 };
|
||||||
|
@ -46,11 +48,15 @@ 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_db[NUM_TONES] = { 4, 4, 4, 4, 4, 4, 5, 5 };
|
||||||
static double tone_diff_ampl_sq[NUM_TONES];
|
static double tone_diff_ampl_sq[NUM_TONES];
|
||||||
|
|
||||||
#define INTERRUPT_DURATION 0.015
|
void dsp_set_sf(double sf_db, double sf_min_db)
|
||||||
#define SPLIT_DURATION 0.030
|
{
|
||||||
#define MF_RECOGNITION 0.025
|
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)
|
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];
|
double tone_amplitude[NUM_TONES];
|
||||||
int t;
|
int t;
|
||||||
|
@ -64,9 +70,14 @@ int dsp_init_inst(dsp_t *dsp, void *priv, const char *name, double samplerate, i
|
||||||
dsp->samplerate = samplerate;
|
dsp->samplerate = samplerate;
|
||||||
dsp->crosstalk = crosstalk;
|
dsp->crosstalk = crosstalk;
|
||||||
dsp->comfort_noise = comfort_noise;
|
dsp->comfort_noise = comfort_noise;
|
||||||
dsp->interrupt_duration = (int)(1000.0 * INTERRUPT_DURATION);
|
// NO interrupt recognition, because the filter itself has a slow response
|
||||||
dsp->split_duration = (int)(1000.0 * SPLIT_DURATION);
|
// dsp->interrupt_recognition = (int)(1000.0 * interrupt_recognition);
|
||||||
dsp->mf_detect_duration = (int)(1000.0 * MF_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->ms_per_sample = 1000.0 / samplerate;
|
||||||
dsp->detect_tone = ' ';
|
dsp->detect_tone = ' ';
|
||||||
|
|
||||||
|
@ -83,7 +94,7 @@ int dsp_init_inst(dsp_t *dsp, void *priv, const char *name, double samplerate, i
|
||||||
return -EINVAL;
|
return -EINVAL;
|
||||||
|
|
||||||
/* init MF demodulator */
|
/* init MF demodulator */
|
||||||
dsp->mf_demod = mf_demod_init(samplerate, NUM_TONES, tone_freq, tone_width);
|
dsp->mf_demod = mf_demod_init(samplerate, NUM_TONES - (!ss5), tone_freq, tone_width);
|
||||||
if (!dsp->mf_mod)
|
if (!dsp->mf_mod)
|
||||||
return -EINVAL;
|
return -EINVAL;
|
||||||
|
|
||||||
|
@ -98,6 +109,12 @@ int dsp_init_inst(dsp_t *dsp, void *priv, const char *name, double samplerate, i
|
||||||
if (rc < 0)
|
if (rc < 0)
|
||||||
abort();
|
abort();
|
||||||
|
|
||||||
|
/* notch filter */
|
||||||
|
if (notch) {
|
||||||
|
dsp->notch = 1;
|
||||||
|
iir_notch_init(&dsp->notch_filter, notch, (int)dsp->samplerate, 1, 4);
|
||||||
|
}
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -150,17 +167,13 @@ static struct dsp_digits {
|
||||||
{ 'a', 0x04 + 0x20 }, /* KP1 */
|
{ 'a', 0x04 + 0x20 }, /* KP1 */
|
||||||
{ 'b', 0x08 + 0x20 }, /* KP2 */
|
{ 'b', 0x08 + 0x20 }, /* KP2 */
|
||||||
{ 'c', 0x10 + 0x20 }, /* ST */
|
{ 'c', 0x10 + 0x20 }, /* ST */
|
||||||
{ 'A', 0x40 }, /* 2400 answer, acknowledge */
|
{ 'B', 0x40 }, /* 2600 busy */
|
||||||
{ 'B', 0x80 }, /* 2600 busy */
|
{ 'A', 0x80 }, /* 2400 answer, acknowledge */
|
||||||
{ 'C', 0x40 + 0x80 }, /* 2600+2400 clear forward */
|
{ 'C', 0x40 + 0x80 }, /* 2600+2400 clear forward */
|
||||||
{ ' ', 0 }, /* silence */
|
{ ' ', 0 }, /* silence */
|
||||||
{ 0 , 0 },
|
{ 0 , 0 },
|
||||||
};
|
};
|
||||||
|
|
||||||
#define KP_DIGIT_DURATION 0.100
|
|
||||||
#define OTHER_DIGIT_DURATION 0.055
|
|
||||||
#define DIGIT_PAUSE 0.055
|
|
||||||
|
|
||||||
/* set signaling tone duration threshold */
|
/* set signaling tone duration threshold */
|
||||||
void set_sig_detect_duration(dsp_t *dsp, double duration_AB, double duration_C)
|
void set_sig_detect_duration(dsp_t *dsp, double duration_AB, double duration_C)
|
||||||
{
|
{
|
||||||
|
@ -170,7 +183,7 @@ void set_sig_detect_duration(dsp_t *dsp, double duration_AB, double duration_C)
|
||||||
}
|
}
|
||||||
|
|
||||||
/* set given tone with duration (ms) or continuously (0) */
|
/* set given tone with duration (ms) or continuously (0) */
|
||||||
void set_tone(dsp_t *dsp, char tone, double duration)
|
int set_tone(dsp_t *dsp, char tone, double duration)
|
||||||
{
|
{
|
||||||
int i;
|
int i;
|
||||||
|
|
||||||
|
@ -178,7 +191,7 @@ void set_tone(dsp_t *dsp, char tone, double duration)
|
||||||
|
|
||||||
if (!tone) {
|
if (!tone) {
|
||||||
PDEBUG_CHAN(DDSP, DEBUG_DEBUG, "Remove tone\n");
|
PDEBUG_CHAN(DDSP, DEBUG_DEBUG, "Remove tone\n");
|
||||||
return;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
for (i = 0; dsp_digits[i].tone; i++) {
|
for (i = 0; dsp_digits[i].tone; i++) {
|
||||||
|
@ -186,12 +199,48 @@ void set_tone(dsp_t *dsp, char tone, double duration)
|
||||||
dsp->tone_mask = dsp_digits[i].mask;
|
dsp->tone_mask = dsp_digits[i].mask;
|
||||||
dsp->tone = tone;
|
dsp->tone = tone;
|
||||||
dsp->tone_duration = (int)(dsp->samplerate * duration);
|
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);
|
PDEBUG_CHAN(DDSP, DEBUG_DEBUG, "Set tone=\'%c\' duration=%.0fms (mask = 0x%02x)\n", dsp->tone, 1000.0 * duration, dsp->tone_mask);
|
||||||
return;
|
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 */
|
/* get next tone from dial string, if any */
|
||||||
static void get_tone_from_dial_string(dsp_t *dsp)
|
static void get_tone_from_dial_string(dsp_t *dsp)
|
||||||
{
|
{
|
||||||
|
@ -200,6 +249,7 @@ static void get_tone_from_dial_string(dsp_t *dsp)
|
||||||
|
|
||||||
dsp->tone = 0;
|
dsp->tone = 0;
|
||||||
|
|
||||||
|
next_digit:
|
||||||
if (dsp->dial_index == dsp->dial_length) {
|
if (dsp->dial_index == dsp->dial_length) {
|
||||||
dsp->dial_length = 0;
|
dsp->dial_length = 0;
|
||||||
dialing_complete(dsp->priv);
|
dialing_complete(dsp->priv);
|
||||||
|
@ -207,33 +257,73 @@ static void get_tone_from_dial_string(dsp_t *dsp)
|
||||||
}
|
}
|
||||||
|
|
||||||
/* get alternating tone/pause from dial string */
|
/* get alternating tone/pause from dial string */
|
||||||
if (!dsp->digit_pause) {
|
if (!dsp->digit_on) {
|
||||||
/* digit on */
|
/* start digit */
|
||||||
|
if (!dsp->pulsedialing) {
|
||||||
|
/* MF digit on */
|
||||||
tone = dsp->dial_string[dsp->dial_index++];
|
tone = dsp->dial_string[dsp->dial_index++];
|
||||||
dsp->digit_pause = 1;
|
|
||||||
if (tone == 'a' || tone == 'b')
|
if (tone == 'a' || tone == 'b')
|
||||||
duration = KP_DIGIT_DURATION;
|
duration = dsp->kp_digit_duration;
|
||||||
else
|
else
|
||||||
duration = OTHER_DIGIT_DURATION;
|
duration = dsp->other_digit_duration;
|
||||||
|
dsp->digit_on = 1;
|
||||||
PDEBUG_CHAN(DDSP, DEBUG_DEBUG, "Send digit \'%c\' from dial string\n", tone);
|
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 {
|
} else {
|
||||||
/* digit pause */
|
/* digit pause */
|
||||||
tone = ' ';
|
tone = ' ';
|
||||||
dsp->digit_pause = 0;
|
dsp->digit_on = 0;
|
||||||
duration = DIGIT_PAUSE;
|
duration = dsp->digit_pause;
|
||||||
PDEBUG_CHAN(DDSP, DEBUG_DEBUG, "Send pause after digit from dial string\n");
|
PDEBUG_CHAN(DDSP, DEBUG_DEBUG, "Send pause after digit from dial string\n");
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
set_tone(dsp, tone, duration);
|
set_tone(dsp, tone, duration);
|
||||||
}
|
}
|
||||||
|
|
||||||
/* set given dial string */
|
/* set given dial string */
|
||||||
void set_dial_string(dsp_t *dsp, const char *dial)
|
void set_dial_string(dsp_t *dsp, const char *dial, int pulsedialing)
|
||||||
{
|
{
|
||||||
dsp->digit_pause = 0;
|
dsp->digit_on = 0;
|
||||||
strncpy(dsp->dial_string, dial, sizeof(dsp->dial_string) - 1);
|
strncpy(dsp->dial_string, dial, sizeof(dsp->dial_string) - 1);
|
||||||
dsp->dial_index = 0;
|
dsp->dial_index = 0;
|
||||||
dsp->dial_length = strlen(dsp->dial_string);
|
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 */
|
/* determine which tones to be modulated, get next tone, if elapsed */
|
||||||
|
@ -267,7 +357,7 @@ static int assemble_tones(dsp_t *dsp, uint32_t *mask, int length)
|
||||||
|
|
||||||
/* detection array for one frequency */
|
/* detection array for one frequency */
|
||||||
static char decode_one[8] =
|
static char decode_one[8] =
|
||||||
{ ' ', ' ', ' ', ' ', ' ', ' ', 'A', 'B' }; /* A = 2400, B = 2600 */
|
{ ' ', ' ', ' ', ' ', ' ', ' ', 'B', 'A' }; /* A = 2400, B = 2600 */
|
||||||
|
|
||||||
/* detection matrix for two frequencies */
|
/* detection matrix for two frequencies */
|
||||||
static char decode_two[8][8] =
|
static char decode_two[8][8] =
|
||||||
|
@ -282,28 +372,36 @@ static char decode_two[8][8] =
|
||||||
{ ' ', ' ', ' ', ' ', ' ', ' ', 'C', ' ' }
|
{ ' ', ' ', ' ', ' ', ' ', ' ', 'C', ' ' }
|
||||||
};
|
};
|
||||||
|
|
||||||
#define NONE_MIN_LEVEL_SQUARED
|
|
||||||
|
|
||||||
/* determine which tone is played */
|
/* determine which tone is played */
|
||||||
static void detect_tones(dsp_t *dsp, sample_t *samples, sample_t **levels_squared, int length, int incoming)
|
static void detect_tones(dsp_t *dsp, sample_t *samples, sample_t **levels_squared, int length, int incoming)
|
||||||
{
|
{
|
||||||
int f1, f2;
|
int f1, f2;
|
||||||
double f1_level_squared, f2_level_squared;
|
double f1_level_squared, f2_level_squared;
|
||||||
char tone;
|
char tone;
|
||||||
int s, t;
|
int s, t, l;
|
||||||
|
|
||||||
for (s = 0; s < length; s++) {
|
for (s = 0; s < length; s++) {
|
||||||
/* mute if split duration reached */
|
/* only perform tone detection/muting every millisecond */
|
||||||
if (dsp->split_duration && dsp->split_count == dsp->split_duration)
|
dsp->ms_interval += dsp->ms_per_sample;
|
||||||
samples[s] = 0.0;
|
dsp->ms_count++;
|
||||||
/* only perform tone detection every millisecond */
|
if (dsp->ms_interval < 1.0)
|
||||||
dsp->detect_interval += dsp->ms_per_sample;
|
|
||||||
if (dsp->detect_interval < 1.0)
|
|
||||||
continue;
|
continue;
|
||||||
dsp->detect_interval -= 1.0;
|
|
||||||
|
|
||||||
if (incoming) {
|
/* 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
|
#ifdef DEBUG_DEMODULATOR
|
||||||
|
if (incoming) {
|
||||||
for (t = 0; t < dsp->mf_demod->tones; t++) {
|
for (t = 0; t < dsp->mf_demod->tones; t++) {
|
||||||
char level[20];
|
char level[20];
|
||||||
int db;
|
int db;
|
||||||
|
@ -315,8 +413,8 @@ static void detect_tones(dsp_t *dsp, sample_t *samples, sample_t **levels_square
|
||||||
printf("%s|", level);
|
printf("%s|", level);
|
||||||
}
|
}
|
||||||
printf("\n");
|
printf("\n");
|
||||||
#endif
|
|
||||||
}
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
/* find the tone which is the loudest */
|
/* find the tone which is the loudest */
|
||||||
f1 = -1;
|
f1 = -1;
|
||||||
|
@ -344,7 +442,10 @@ static void detect_tones(dsp_t *dsp, sample_t *samples, sample_t **levels_square
|
||||||
if (f2 >= 0 && f2_level_squared < tone_min_ampl_sq[f2])
|
if (f2 >= 0 && f2_level_squared < tone_min_ampl_sq[f2])
|
||||||
f2 = -1;
|
f2 = -1;
|
||||||
|
|
||||||
// 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)));
|
#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 */
|
/* check if no, one or two tones are detected */
|
||||||
if (f1 < 0)
|
if (f1 < 0)
|
||||||
tone = ' ';
|
tone = ' ';
|
||||||
|
@ -356,25 +457,34 @@ static void detect_tones(dsp_t *dsp, sample_t *samples, sample_t **levels_square
|
||||||
else
|
else
|
||||||
tone = decode_two[f1][f2];
|
tone = decode_two[f1][f2];
|
||||||
}
|
}
|
||||||
//printf("tone=%c\n", tone);
|
|
||||||
|
|
||||||
/* process interrupt counting, keep tone until interrupt counter expires */
|
/* process interrupt counting, keep tone until interrupt counter expires */
|
||||||
|
if (dsp->interrupt_recognition) {
|
||||||
if (dsp->detect_tone != ' ' && tone != dsp->detect_tone) {
|
if (dsp->detect_tone != ' ' && tone != dsp->detect_tone) {
|
||||||
if (dsp->interrupt_count < dsp->interrupt_duration) {
|
#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++;
|
dsp->interrupt_count++;
|
||||||
tone = dsp->detect_tone;
|
tone = dsp->detect_tone;
|
||||||
}
|
}
|
||||||
} else
|
} 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;
|
dsp->interrupt_count = 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/* split audio, after minimum duration of detecting a tone */
|
/* split audio, after minimum duration of detecting a tone */
|
||||||
if (tone >= 'A' && tone <= 'C') {
|
if (tone >= 'A' && tone <= 'C') {
|
||||||
if (dsp->split_count < dsp->split_duration)
|
if (dsp->split_count < dsp->split_recognition)
|
||||||
dsp->split_count++;
|
dsp->split_count++;
|
||||||
} else
|
} else
|
||||||
dsp->split_count = 0;
|
dsp->split_count = 0;
|
||||||
|
|
||||||
|
|
||||||
/* some change in tone */
|
/* some change in tone */
|
||||||
if (dsp->detect_tone != tone) {
|
if (dsp->detect_tone != tone) {
|
||||||
if (dsp->detect_count == 0)
|
if (dsp->detect_count == 0)
|
||||||
|
@ -386,10 +496,14 @@ static void detect_tones(dsp_t *dsp, sample_t *samples, sample_t **levels_square
|
||||||
if (dsp->detect_count < dsp->sig_detect_duration_AB)
|
if (dsp->detect_count < dsp->sig_detect_duration_AB)
|
||||||
dsp->detect_count++;
|
dsp->detect_count++;
|
||||||
else {
|
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 */
|
/* sign tone detected */
|
||||||
dsp->detect_count = 0;
|
dsp->detect_count = 0;
|
||||||
dsp->detect_tone = tone;
|
dsp->detect_tone = tone;
|
||||||
receive_digit(dsp->priv, tone, level2db(sqrt(f1_level_squared)));
|
receive_signal(dsp->priv, tone, level2db(sqrt(f1_level_squared)));
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
case 'C':
|
case 'C':
|
||||||
|
@ -400,29 +514,41 @@ static void detect_tones(dsp_t *dsp, sample_t *samples, sample_t **levels_square
|
||||||
/* sign tone detected */
|
/* sign tone detected */
|
||||||
dsp->detect_count = 0;
|
dsp->detect_count = 0;
|
||||||
dsp->detect_tone = tone;
|
dsp->detect_tone = tone;
|
||||||
receive_digit(dsp->priv, tone, level2db(sqrt(f1_level_squared)));
|
receive_signal(dsp->priv, tone, level2db(sqrt(f1_level_squared)));
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
case ' ':
|
case ' ':
|
||||||
/* tone appears or ceases */
|
/* tone ceases, there is no counting here, since interrupt counting does the job for us */
|
||||||
dsp->detect_count = 0;
|
dsp->detect_count = 0;
|
||||||
dsp->detect_tone = tone;
|
dsp->detect_tone = tone;
|
||||||
receive_digit(dsp->priv, tone, 0.0);
|
if (incoming)
|
||||||
|
PDEBUG_CHAN(DDSP, DEBUG_DEBUG, "cease\n");
|
||||||
|
receive_signal(dsp->priv, tone, 0.0);
|
||||||
break;
|
break;
|
||||||
default:
|
default:
|
||||||
/* tone appears, wait some time */
|
/* tone appears, wait some time */
|
||||||
if (dsp->detect_count < dsp->mf_detect_duration)
|
if (dsp->detect_count < dsp->mf_recognition)
|
||||||
dsp->detect_count++;
|
dsp->detect_count++;
|
||||||
else {
|
else {
|
||||||
/* sign tone detected */
|
/* sign tone detected */
|
||||||
dsp->detect_count = 0;
|
dsp->detect_count = 0;
|
||||||
dsp->detect_tone = tone;
|
dsp->detect_tone = tone;
|
||||||
receive_digit(dsp->priv, tone, level2db(sqrt(f1_level_squared)));
|
receive_signal(dsp->priv, tone, level2db(sqrt(f1_level_squared)));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
} else
|
} else
|
||||||
dsp->detect_count = 0;
|
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) */
|
/* process audio from one link (source) to another (destination) */
|
||||||
|
@ -440,33 +566,27 @@ static void process_audio(dsp_t *dsp_a, dsp_t *dsp_b, int length)
|
||||||
jitter_load(&dsp_a->tx_dejitter, samples[0], length);
|
jitter_load(&dsp_a->tx_dejitter, samples[0], length);
|
||||||
jitter_load(&dsp_b->tx_dejitter, samples[1], length);
|
jitter_load(&dsp_b->tx_dejitter, samples[1], length);
|
||||||
|
|
||||||
/* optionally add comfort noise */
|
/* optionally add (-36 dBm) comfort noise */
|
||||||
if (!dsp_a->cc_callref && dsp_a->comfort_noise) {
|
if (!dsp_a->cc_callref && dsp_a->comfort_noise) {
|
||||||
for (i = 0; i < length; i++)
|
for (i = 0; i < length; i++)
|
||||||
samples[0][i] += (double)((int8_t)random()) / 8000.0;
|
samples[0][i] += (double)((int8_t)random()) / 16384.0;
|
||||||
}
|
}
|
||||||
if (!dsp_b->cc_callref && dsp_b->comfort_noise) {
|
if (!dsp_b->cc_callref && dsp_b->comfort_noise) {
|
||||||
for (i = 0; i < length; i++)
|
for (i = 0; i < length; i++)
|
||||||
samples[1][i] += (double)((int8_t)random()) / 8000.0;
|
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 */
|
/* 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);
|
count1 = assemble_tones(dsp_a, mask, length);
|
||||||
mf_mod(dsp_a->mf_mod, mask, samples[0], count1);
|
mf_mod(dsp_a->mf_mod, mask, samples[0], count1, dsp_a->tone_transparent);
|
||||||
count2 = assemble_tones(dsp_b, mask, length);
|
count2 = assemble_tones(dsp_b, mask, length);
|
||||||
mf_mod(dsp_b->mf_mod, mask, samples[1], count2);
|
mf_mod(dsp_b->mf_mod, mask, samples[1], count2, dsp_b->tone_transparent);
|
||||||
|
|
||||||
/* 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;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* ! here is the bridge from a to b and from b to a ! */
|
/* ! here is the bridge from a to b and from b to a ! */
|
||||||
|
|
||||||
|
@ -492,9 +612,21 @@ static void process_audio(dsp_t *dsp_a, dsp_t *dsp_b, int length)
|
||||||
|
|
||||||
/* demodulate and call tone detector */
|
/* demodulate and call tone detector */
|
||||||
mf_demod(dsp_b->mf_demod, samples[0], length, levels_squared);
|
mf_demod(dsp_b->mf_demod, samples[0], length, levels_squared);
|
||||||
detect_tones(dsp_b->priv, samples[0], levels_squared, length, 1);
|
detect_tones(dsp_b, samples[0], levels_squared, length, 1);
|
||||||
mf_demod(dsp_a->mf_demod, samples[1], length, levels_squared);
|
mf_demod(dsp_a->mf_demod, samples[1], length, levels_squared);
|
||||||
detect_tones(dsp_a->priv, samples[1], levels_squared, length, 0);
|
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 */
|
/* forward audio to CC if call exists */
|
||||||
if (dsp_b->cc_callref && dsp_b->codec) {
|
if (dsp_b->cc_callref && dsp_b->codec) {
|
||||||
|
|
|
@ -16,24 +16,35 @@ typedef struct dsp {
|
||||||
/* tone generation */
|
/* tone generation */
|
||||||
mf_mod_t *mf_mod; /* MF modulator */
|
mf_mod_t *mf_mod; /* MF modulator */
|
||||||
char tone; /* current digit playing or 0 */
|
char tone; /* current digit playing or 0 */
|
||||||
|
int tone_transparent; /* mix tone with audio */
|
||||||
uint32_t tone_mask; /* bit-mask of which MF tones to play */
|
uint32_t tone_mask; /* bit-mask of which MF tones to play */
|
||||||
int tone_duration; /* counter of samples for length of tone */
|
int tone_duration; /* counter of samples for length of tone */
|
||||||
char dial_string[33]; /* stores digits when dialing number sequence */
|
char dial_string[33]; /* stores digits when dialing number sequence */
|
||||||
int dial_length; /* length of dial string, or 0 */
|
int dial_length; /* length of dial string, or 0 */
|
||||||
int dial_index; /* current position at dial string */
|
int dial_index; /* current position at dial string */
|
||||||
int digit_pause; /* flag that states if pause is played after digit */
|
int digit_on; /* flag that states if digit is currently playing */
|
||||||
|
double kp_digit_duration; /* duration of kp digit */
|
||||||
|
double other_digit_duration; /* duration of other digits */
|
||||||
|
double digit_pause; /* interdigit pause between */
|
||||||
|
int pulsedialing; /* using pulses to dial */
|
||||||
|
int pulse_on; /* flag that states if pulse is currently on */
|
||||||
|
int pulse_num; /* number of pulses to send */
|
||||||
|
int pulse_count; /* counter for pulses in one digit */
|
||||||
|
double pulse_pause; /* interdigit pause between pulse digits */
|
||||||
|
int sit_on, sit_count; /* sit tone generator */
|
||||||
|
|
||||||
/* tone detection */
|
/* tone detection */
|
||||||
mf_demod_t *mf_demod; /* MF demodulator */
|
mf_demod_t *mf_demod; /* MF demodulator */
|
||||||
double ms_per_sample; /* how many milliseconds between two samples */
|
double ms_per_sample; /* how many milliseconds between two samples */
|
||||||
double detect_interval; /* counts milliseconds */
|
double ms_interval; /* counts milliseconds */
|
||||||
int interrupt_duration; /* number of milliseconds until interruption is detected */
|
int ms_count; /* counts samples within millisecond interval */
|
||||||
|
int interrupt_recognition; /* number of milliseconds until interruption is detected */
|
||||||
int interrupt_count; /* counter for interrupt condition */
|
int interrupt_count; /* counter for interrupt condition */
|
||||||
int split_duration; /* number of milliseconds until split (mute) condition meats */
|
int split_recognition; /* number of milliseconds until split (mute) condition meats */
|
||||||
int split_count; /* counter for split condition */
|
int split_count; /* counter for split condition */
|
||||||
int sig_detect_duration_AB; /* signaling tone duration in milliseconds (TONE A/B) */
|
int sig_detect_duration_AB; /* signaling tone duration in milliseconds (TONE A/B) */
|
||||||
int sig_detect_duration_C; /* signaling tone duration in milliseconds (TONE C) */
|
int sig_detect_duration_C; /* signaling tone duration in milliseconds (TONE C) */
|
||||||
int mf_detect_duration; /* MF tone duration in milliseconds */
|
int mf_recognition; /* MF tone duration in milliseconds */
|
||||||
int detect_count; /* counter for tone detection */
|
int detect_count; /* counter for tone detection */
|
||||||
char detect_tone; /* current tone detected or ' ' for no tone */
|
char detect_tone; /* current tone detected or ' ' for no tone */
|
||||||
|
|
||||||
|
@ -44,6 +55,8 @@ typedef struct dsp {
|
||||||
int delay_index;
|
int delay_index;
|
||||||
int crosstalk; /* mix crosstalk from TX to RX */
|
int crosstalk; /* mix crosstalk from TX to RX */
|
||||||
int comfort_noise; /* add comfort noise before answer and after disconnect */
|
int comfort_noise; /* add comfort noise before answer and after disconnect */
|
||||||
|
int notch;
|
||||||
|
iir_filter_t notch_filter; /* remove 2600 Hz */
|
||||||
|
|
||||||
/* osmo-CC */
|
/* osmo-CC */
|
||||||
uint32_t cc_callref; /* ref to CC call */
|
uint32_t cc_callref; /* ref to CC call */
|
||||||
|
@ -51,16 +64,19 @@ typedef struct dsp {
|
||||||
osmo_cc_session_codec_t *codec; /* selected codec */
|
osmo_cc_session_codec_t *codec; /* selected codec */
|
||||||
} dsp_t;
|
} dsp_t;
|
||||||
|
|
||||||
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);
|
void dsp_set_sf(double sf_db, double 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);
|
||||||
void dsp_cleanup_inst(dsp_t *dsp);
|
void dsp_cleanup_inst(dsp_t *dsp);
|
||||||
void set_sig_detect_duration(dsp_t *dsp, double duration_AB, double duration_C);
|
void set_sig_detect_duration(dsp_t *dsp, double duration_AB, double duration_C);
|
||||||
void set_tone(dsp_t *dsp, char tone, double duration);
|
int set_tone(dsp_t *dsp, char tone, double duration);
|
||||||
void set_dial_string(dsp_t *dsp, const char *dial);
|
void set_tone_transparent(dsp_t *dsp, int transparent);
|
||||||
|
void set_sit(dsp_t *dsp, int on);
|
||||||
|
void set_dial_string(dsp_t *dsp, const char *dial, int pulsedialing);
|
||||||
void audio_clock(dsp_t *sunset_list, dsp_t *sunrise_list, int len);
|
void audio_clock(dsp_t *sunset_list, dsp_t *sunrise_list, int len);
|
||||||
void down_audio(struct osmo_cc_session_codec *codec, uint8_t marker, uint16_t sequence, uint32_t timestamp, uint32_t ssrc, uint8_t *data, int len);
|
void down_audio(struct osmo_cc_session_codec *codec, uint8_t marker, uint16_t sequence, uint32_t timestamp, uint32_t ssrc, uint8_t *data, int len);
|
||||||
void encode_l16(uint8_t *src_data, int src_len, uint8_t **dst_data, int *dst_len, void *priv);
|
void encode_l16(uint8_t *src_data, int src_len, uint8_t **dst_data, int *dst_len, void *priv);
|
||||||
void decode_l16(uint8_t *src_data, int src_len, uint8_t **dst_data, int *dst_len, void *priv);
|
void decode_l16(uint8_t *src_data, int src_len, uint8_t **dst_data, int *dst_len, void *priv);
|
||||||
|
|
||||||
void receive_digit(void *priv, char digit, double dbm);
|
void receive_signal(void *priv, char digit, double dbm);
|
||||||
void dialing_complete(void *priv);
|
void dialing_complete(void *priv);
|
||||||
|
|
||||||
|
|
|
@ -100,12 +100,13 @@ void mf_mod_exit(mf_mod_t *mod)
|
||||||
free(mod);
|
free(mod);
|
||||||
}
|
}
|
||||||
|
|
||||||
void mf_mod(mf_mod_t *mod, uint32_t *mask, sample_t *samples, int length)
|
void mf_mod(mf_mod_t *mod, uint32_t *mask, sample_t *samples, int length, int transparent)
|
||||||
{
|
{
|
||||||
int t, s;
|
int t, s;
|
||||||
double phase;
|
double phase;
|
||||||
|
|
||||||
for (s = 0; s < length; s++) {
|
for (s = 0; s < length; s++) {
|
||||||
|
if (!transparent)
|
||||||
samples[s] = 0;
|
samples[s] = 0;
|
||||||
for (t = 0; t < mod->tones; t++) {
|
for (t = 0; t < mod->tones; t++) {
|
||||||
/* continue phase even on muted tones */
|
/* continue phase even on muted tones */
|
||||||
|
|
|
@ -27,7 +27,7 @@ int mf_init(int _fast_math);
|
||||||
void mf_exit(void);
|
void mf_exit(void);
|
||||||
mf_mod_t *mf_mod_init(double samplerate, int tones, double *freq, double *amplitude);
|
mf_mod_t *mf_mod_init(double samplerate, int tones, double *freq, double *amplitude);
|
||||||
void mf_mod_exit(mf_mod_t *mod);
|
void mf_mod_exit(mf_mod_t *mod);
|
||||||
void mf_mod(mf_mod_t *mod, uint32_t *mask, sample_t *samples, int length);
|
void mf_mod(mf_mod_t *mod, uint32_t *mask, sample_t *samples, int length, int transparent);
|
||||||
mf_demod_t *mf_demod_init(double samplerate, int tones, double *freq, double *width);
|
mf_demod_t *mf_demod_init(double samplerate, int tones, double *freq, double *width);
|
||||||
void mf_demod_exit(mf_demod_t *demod);
|
void mf_demod_exit(mf_demod_t *demod);
|
||||||
void mf_demod(mf_demod_t *demod, sample_t *samples, int length, sample_t **levels_squared);
|
void mf_demod(mf_demod_t *demod, sample_t *samples, int length, sample_t **levels_squared);
|
||||||
|
|
|
@ -0,0 +1,53 @@
|
||||||
|
|
||||||
|
#include <stdlib.h>
|
||||||
|
#include <math.h>
|
||||||
|
#include "../libsample/sample.h"
|
||||||
|
#include "sit.h"
|
||||||
|
|
||||||
|
#define db2level(db) pow(10, (double)(db) / 20.0)
|
||||||
|
|
||||||
|
#define SIT_F1 950.0
|
||||||
|
#define SIT_F2 1400.0
|
||||||
|
#define SIT_F3 1800.0
|
||||||
|
#define SIT_LEVEL -24.0 /* -24 dBm */
|
||||||
|
#define SIT_DURATION 0.330 /* tone duration */
|
||||||
|
#define SIT_PAUSE 1.0 /* tone pause */
|
||||||
|
|
||||||
|
static sample_t *sit_samples;
|
||||||
|
static int sit_length = 0;
|
||||||
|
|
||||||
|
void init_sit(int samplerate)
|
||||||
|
{
|
||||||
|
int length, i;
|
||||||
|
double level = db2level(SIT_LEVEL);
|
||||||
|
|
||||||
|
length = (int)((SIT_DURATION * 3 + SIT_PAUSE) * (double)samplerate);
|
||||||
|
sit_samples = calloc(length, sizeof(*sit_samples));
|
||||||
|
if (!sit_samples)
|
||||||
|
abort();
|
||||||
|
sit_length = length;
|
||||||
|
|
||||||
|
length = (int)(SIT_DURATION * (double)samplerate);
|
||||||
|
for (i = 0; i < length; i++)
|
||||||
|
sit_samples[i] = sin(2.0 * M_PI * SIT_F1 * (double)i / (double)samplerate) * level;
|
||||||
|
for (; i < length * 2; i++)
|
||||||
|
sit_samples[i] = sin(2.0 * M_PI * SIT_F2 * (double)i / (double)samplerate) * level;
|
||||||
|
for (; i < length * 3; i++)
|
||||||
|
sit_samples[i] = sin(2.0 * M_PI * SIT_F3 * (double)i / (double)samplerate) * level;
|
||||||
|
}
|
||||||
|
|
||||||
|
void exit_sit(void)
|
||||||
|
{
|
||||||
|
free(sit_samples);
|
||||||
|
sit_length = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
int sit_play(sample_t *samples, int count, int length)
|
||||||
|
{
|
||||||
|
while (length--) {
|
||||||
|
*samples++ = sit_samples[count];
|
||||||
|
if (++count == sit_length)
|
||||||
|
count = 0;
|
||||||
|
}
|
||||||
|
return count;
|
||||||
|
}
|
|
@ -0,0 +1,5 @@
|
||||||
|
|
||||||
|
void init_sit(int samplerate);
|
||||||
|
void exit_sit(void);
|
||||||
|
int sit_play(sample_t *samples, int count, int length);
|
||||||
|
|
|
@ -5,7 +5,6 @@ bin_PROGRAMS = \
|
||||||
|
|
||||||
osmo_cc_ss5_endpoint_SOURCES = \
|
osmo_cc_ss5_endpoint_SOURCES = \
|
||||||
ss5.c \
|
ss5.c \
|
||||||
display_status.c \
|
|
||||||
main.c
|
main.c
|
||||||
|
|
||||||
osmo_cc_ss5_endpoint_LDADD = \
|
osmo_cc_ss5_endpoint_LDADD = \
|
||||||
|
|
|
@ -29,7 +29,7 @@
|
||||||
#include "../liboptions/options.h"
|
#include "../liboptions/options.h"
|
||||||
#include "../libg711/g711.h"
|
#include "../libg711/g711.h"
|
||||||
#include "ss5.h"
|
#include "ss5.h"
|
||||||
#include "display.h"
|
#include "../common/display.h"
|
||||||
|
|
||||||
ss5_endpoint_t *ss5_ep_sunset = NULL, *ss5_ep_sunrise = NULL;
|
ss5_endpoint_t *ss5_ep_sunset = NULL, *ss5_ep_sunrise = NULL;
|
||||||
int num_kanal = 2;
|
int num_kanal = 2;
|
||||||
|
@ -80,16 +80,16 @@ static void print_help()
|
||||||
printf(" Prevent blueboxing by making 'release-guard' 200 ms minimum length.\n");
|
printf(" Prevent blueboxing by making 'release-guard' 200 ms minimum length.\n");
|
||||||
printf(" -x --crosstalk 1 | 0\n");
|
printf(" -x --crosstalk 1 | 0\n");
|
||||||
printf(" Enable or disable some minor crosstalk. This allows you to hear\n");
|
printf(" Enable or disable some minor crosstalk. This allows you to hear\n");
|
||||||
printf(" transmitted tones at a low volume. (Default is %d.).\n", crosstalk);
|
printf(" transmitted tones at a low volume. (Default is %d.)\n", crosstalk);
|
||||||
printf(" -d --delay <ms> | 0\n");
|
printf(" -d --delay <ms> | 0\n");
|
||||||
printf(" Add one-way delay to the connection between two SS5 links. This allows\n");
|
printf(" Add one-way delay to the connection between two SS5 links. This allows\n");
|
||||||
printf(" to hear 'acknowlege' tones delayed. (Default is %d ms.).\n", delay_ms);
|
printf(" to hear 'acknowlege' tones delayed. (Default is %d ms.)\n", delay_ms);
|
||||||
printf(" -n --comfort-noise 1 | 0\n");
|
printf(" -n --comfort-noise 1 | 0\n");
|
||||||
printf(" Add comfort noise whenever there is no audio from the remote link\n");
|
printf(" Add comfort noise whenever there is no audio from the remote link\n");
|
||||||
printf(" (before or after call). (Default is %d ms.).\n", comfort_noise);
|
printf(" (before or after call). (Default is %d ms.)\n", comfort_noise);
|
||||||
printf(" --sense 0 | <db>\n");
|
printf(" --sense 0 | <db>\n");
|
||||||
printf(" Change sensitivity (level) of tone detector. A bluebox must not be\n");
|
printf(" Increase sensitivity of tone detector. A bluebox can have lower level\n");
|
||||||
printf(" that loud. (Default is %.0f dB.).\n", sense_db);
|
printf(" than what the standard requires. (Default is %.0f dB.)\n", sense_db);
|
||||||
printf(" -C --cc \"<osmo-cc arg>\" [--cc ...]\n");
|
printf(" -C --cc \"<osmo-cc arg>\" [--cc ...]\n");
|
||||||
printf(" --cc2 \"<osmo-cc arg>\" [--cc2 ...]\n");
|
printf(" --cc2 \"<osmo-cc arg>\" [--cc2 ...]\n");
|
||||||
printf(" Pass arguments to Osmo-CC endpoint. Use '-cc help' for description.\n");
|
printf(" Pass arguments to Osmo-CC endpoint. Use '-cc help' for description.\n");
|
||||||
|
@ -234,7 +234,7 @@ static void clock_timeout(void __attribute__((unused)) *data)
|
||||||
timer_start(&clock_timer, last_time_clock - now);
|
timer_start(&clock_timer, last_time_clock - now);
|
||||||
|
|
||||||
/* call audio clock every 20ms */
|
/* call audio clock every 20ms */
|
||||||
audio_clock(ss5_ep_sunset->dsp_list, ss5_ep_sunrise->dsp_list, 160);
|
audio_clock((ss5_ep_sunset) ? ss5_ep_sunset->dsp_list : NULL, (ss5_ep_sunrise) ? ss5_ep_sunrise->dsp_list : NULL, 160);
|
||||||
|
|
||||||
/* process keyboard input */
|
/* process keyboard input */
|
||||||
c = get_char();
|
c = get_char();
|
||||||
|
@ -259,6 +259,9 @@ int main(int argc, char *argv[])
|
||||||
/* init codecs */
|
/* init codecs */
|
||||||
g711_init();
|
g711_init();
|
||||||
|
|
||||||
|
/* init dsp */
|
||||||
|
dsp_set_sf(-9.0, -16.0);
|
||||||
|
|
||||||
cc_argv_sunset[cc_argc_sunset++] = options_strdup("remote auto");
|
cc_argv_sunset[cc_argc_sunset++] = options_strdup("remote auto");
|
||||||
cc_argv_sunrise[cc_argc_sunrise++] = options_strdup("remote auto");
|
cc_argv_sunrise[cc_argc_sunrise++] = options_strdup("remote auto");
|
||||||
|
|
||||||
|
|
475
src/ss5/ss5.c
475
src/ss5/ss5.c
|
@ -30,7 +30,8 @@
|
||||||
#include "../libdebug/debug.h"
|
#include "../libdebug/debug.h"
|
||||||
#include "../libg711/g711.h"
|
#include "../libg711/g711.h"
|
||||||
#include "ss5.h"
|
#include "ss5.h"
|
||||||
#include "display.h"
|
#include "../common/display.h"
|
||||||
|
#include "../common/common.h"
|
||||||
|
|
||||||
/* names of all SS5 states */
|
/* names of all SS5 states */
|
||||||
const char *ss5_state_names[] = {
|
const char *ss5_state_names[] = {
|
||||||
|
@ -65,6 +66,12 @@ const char *ss5_state_names[] = {
|
||||||
};
|
};
|
||||||
|
|
||||||
/* timers and durations */
|
/* timers and durations */
|
||||||
|
#define INTERRUPT_RECOGNITION 0.015
|
||||||
|
#define SPLIT_RECOGNITION 0.030
|
||||||
|
#define MF_RECOGNITION 0.025
|
||||||
|
#define KP_DIGIT_DURATION 0.100
|
||||||
|
#define OTHER_DIGIT_DURATION 0.055
|
||||||
|
#define DIGIT_PAUSE 0.055
|
||||||
#define SIGN_RECOGNITION_FAST 0.040 /* 40 ms for seize and proceed-to-send */
|
#define SIGN_RECOGNITION_FAST 0.040 /* 40 ms for seize and proceed-to-send */
|
||||||
#define SIGN_RECOGNITION_NORMAL 0.125 /* 125 ms for all other signals */
|
#define SIGN_RECOGNITION_NORMAL 0.125 /* 125 ms for all other signals */
|
||||||
#define MIN_RELEASE_GUARD 0.200 /* minimum 200 ms, in case we prevent blueboxing */
|
#define MIN_RELEASE_GUARD 0.200 /* minimum 200 ms, in case we prevent blueboxing */
|
||||||
|
@ -101,10 +108,10 @@ void refresh_status(void)
|
||||||
for (ep = osmo_cc_endpoint_list; ep; ep = ep->next) {
|
for (ep = osmo_cc_endpoint_list; ep; ep = ep->next) {
|
||||||
ss5_ep = ep->priv;
|
ss5_ep = ep->priv;
|
||||||
if (!ss5_ep->dsp_list)
|
if (!ss5_ep->dsp_list)
|
||||||
display_status_line(ep->local_name, 0, NULL, NULL, 0);
|
display_status_line(ep->local_name, 0, NULL, NULL, "");
|
||||||
for (i = 0, dsp = ss5_ep->dsp_list; dsp; i++, dsp = dsp->next) {
|
for (i = 0, dsp = ss5_ep->dsp_list; dsp; i++, dsp = dsp->next) {
|
||||||
ss5 = dsp->priv;
|
ss5 = dsp->priv;
|
||||||
display_status_line(ep->local_name, i, ss5->callerid, ss5->dialing, ss5->state);
|
display_status_line(ep->local_name, i, ss5->callerid, ss5->dialing, ss5_state_names[ss5->state]);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -172,7 +179,7 @@ static ss5_t *link_create(ss5_endpoint_t *ss5_ep, const char *ep_name, int linki
|
||||||
snprintf(ss5->name, sizeof(ss5->name) - 1, "%s/%d", ep_name, linkid);
|
snprintf(ss5->name, sizeof(ss5->name) - 1, "%s/%d", ep_name, linkid);
|
||||||
|
|
||||||
/* init dsp instance */
|
/* init dsp instance */
|
||||||
dsp_init_inst(&ss5->dsp, ss5, ss5->name, samplerate, crosstalk, comfort_noise, delay_ms, sense_db);
|
dsp_init_inst(&ss5->dsp, ss5, ss5->name, samplerate, crosstalk, comfort_noise, delay_ms, sense_db, INTERRUPT_RECOGNITION, SPLIT_RECOGNITION, MF_RECOGNITION, KP_DIGIT_DURATION, OTHER_DIGIT_DURATION, DIGIT_PAUSE, 0.0, 0.0, 1);
|
||||||
|
|
||||||
/* init timer */
|
/* init timer */
|
||||||
timer_init(&ss5->timer, ss5_timeout, ss5);
|
timer_init(&ss5->timer, ss5_timeout, ss5);
|
||||||
|
@ -208,7 +215,7 @@ static void link_destroy(ss5_t *ss5)
|
||||||
*dsp_p = ss5->dsp.next;
|
*dsp_p = ss5->dsp.next;
|
||||||
|
|
||||||
/* state idle */
|
/* state idle */
|
||||||
ss5_new_state(ss5, SS5_STATE_IDLE);
|
ss5_new_state(ss5, SS5_STATE_NULL);
|
||||||
|
|
||||||
/* reset instance */
|
/* reset instance */
|
||||||
link_reset(ss5);
|
link_reset(ss5);
|
||||||
|
@ -239,7 +246,7 @@ ss5_endpoint_t *ss5_ep_create(const char *ep_name, int links, int prevent_bluebo
|
||||||
ss5_ep->suppress_disconnect = suppress_disconnect;
|
ss5_ep->suppress_disconnect = suppress_disconnect;
|
||||||
|
|
||||||
for (i = 0; i < links; i++)
|
for (i = 0; i < links; i++)
|
||||||
link_create(ss5_ep, ep_name, i + 1, 8000, crosstalk, comfort_noise, delay_ms, sense_db);
|
link_create(ss5_ep, ep_name, i + 1, 8000.0, crosstalk, comfort_noise, delay_ms, sense_db);
|
||||||
|
|
||||||
PDEBUG(DSS5, DEBUG_DEBUG, "SS5 endpoint instance created\n");
|
PDEBUG(DSS5, DEBUG_DEBUG, "SS5 endpoint instance created\n");
|
||||||
|
|
||||||
|
@ -257,335 +264,30 @@ void ss5_ep_destroy(ss5_endpoint_t *ss5_ep)
|
||||||
PDEBUG(DSS5, DEBUG_DEBUG, "SS5 endpoint instance destroyed\n");
|
PDEBUG(DSS5, DEBUG_DEBUG, "SS5 endpoint instance destroyed\n");
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
|
||||||
* several messages towards CC
|
|
||||||
*/
|
|
||||||
|
|
||||||
static void reject_call(ss5_endpoint_t *ss5_ep, uint32_t callref, uint8_t isdn_cause)
|
|
||||||
|
|
||||||
{
|
|
||||||
osmo_cc_msg_t *new_msg;
|
|
||||||
|
|
||||||
/* create osmo-cc message */
|
|
||||||
new_msg = osmo_cc_new_msg(OSMO_CC_MSG_REJ_IND);
|
|
||||||
/* cause */
|
|
||||||
osmo_cc_add_ie_cause(new_msg, OSMO_CC_LOCATION_BEYOND_INTERWORKING, isdn_cause, 0, 0);
|
|
||||||
/* send message to osmo-cc */
|
|
||||||
osmo_cc_ll_msg(&ss5_ep->cc_ep, callref, new_msg);
|
|
||||||
}
|
|
||||||
|
|
||||||
static void release_call(ss5_t *ss5, uint8_t isdn_cause)
|
|
||||||
{
|
|
||||||
osmo_cc_msg_t *new_msg;
|
|
||||||
|
|
||||||
/* create osmo-cc message */
|
|
||||||
new_msg = osmo_cc_new_msg(OSMO_CC_MSG_REL_IND);
|
|
||||||
/* cause */
|
|
||||||
osmo_cc_add_ie_cause(new_msg, OSMO_CC_LOCATION_BEYOND_INTERWORKING, isdn_cause, 0, 0);
|
|
||||||
/* send message to osmo-cc */
|
|
||||||
osmo_cc_ll_msg(&ss5->ss5_ep->cc_ep, ss5->dsp.cc_callref, new_msg);
|
|
||||||
}
|
|
||||||
|
|
||||||
static void disconnect_call(ss5_t *ss5, uint8_t isdn_cause)
|
|
||||||
{
|
|
||||||
osmo_cc_msg_t *new_msg;
|
|
||||||
|
|
||||||
if (ss5->ss5_ep->suppress_disconnect)
|
|
||||||
return;
|
|
||||||
/* create osmo-cc message */
|
|
||||||
new_msg = osmo_cc_new_msg(OSMO_CC_MSG_DISC_IND);
|
|
||||||
/* progress */
|
|
||||||
osmo_cc_add_ie_progress(new_msg, OSMO_CC_CODING_ITU_T, OSMO_CC_LOCATION_BEYOND_INTERWORKING, OSMO_CC_PROGRESS_INBAND_INFO_AVAILABLE);
|
|
||||||
/* cause */
|
|
||||||
osmo_cc_add_ie_cause(new_msg, OSMO_CC_LOCATION_BEYOND_INTERWORKING, isdn_cause, 0, 0);
|
|
||||||
/* send message to osmo-cc */
|
|
||||||
osmo_cc_ll_msg(&ss5->ss5_ep->cc_ep, ss5->dsp.cc_callref, new_msg);
|
|
||||||
}
|
|
||||||
|
|
||||||
static void proceed_call(ss5_t *ss5, const char *sdp)
|
|
||||||
{
|
|
||||||
osmo_cc_msg_t *new_msg;
|
|
||||||
|
|
||||||
/* create osmo-cc message */
|
|
||||||
new_msg = osmo_cc_new_msg(OSMO_CC_MSG_PROC_IND);
|
|
||||||
/* progress */
|
|
||||||
osmo_cc_add_ie_progress(new_msg, OSMO_CC_CODING_ITU_T, OSMO_CC_LOCATION_BEYOND_INTERWORKING, OSMO_CC_PROGRESS_INBAND_INFO_AVAILABLE);
|
|
||||||
/* sdp */
|
|
||||||
osmo_cc_add_ie_sdp(new_msg, sdp);
|
|
||||||
/* send message to osmo-cc */
|
|
||||||
osmo_cc_ll_msg(&ss5->ss5_ep->cc_ep, ss5->dsp.cc_callref, new_msg);
|
|
||||||
}
|
|
||||||
|
|
||||||
static void alert_call(ss5_t *ss5)
|
|
||||||
{
|
|
||||||
osmo_cc_msg_t *new_msg;
|
|
||||||
|
|
||||||
/* create osmo-cc message */
|
|
||||||
new_msg = osmo_cc_new_msg(OSMO_CC_MSG_ALERT_IND);
|
|
||||||
/* send message to osmo-cc */
|
|
||||||
osmo_cc_ll_msg(&ss5->ss5_ep->cc_ep, ss5->dsp.cc_callref, new_msg);
|
|
||||||
}
|
|
||||||
|
|
||||||
static void answer_call(ss5_t *ss5)
|
|
||||||
{
|
|
||||||
osmo_cc_msg_t *new_msg;
|
|
||||||
|
|
||||||
/* create osmo-cc message */
|
|
||||||
new_msg = osmo_cc_new_msg(OSMO_CC_MSG_SETUP_CNF);
|
|
||||||
/* send message to osmo-cc */
|
|
||||||
osmo_cc_ll_msg(&ss5->ss5_ep->cc_ep, ss5->dsp.cc_callref, new_msg);
|
|
||||||
}
|
|
||||||
|
|
||||||
static void setup_comp_call(ss5_t *ss5)
|
|
||||||
{
|
|
||||||
osmo_cc_msg_t *new_msg;
|
|
||||||
|
|
||||||
/* create osmo-cc message */
|
|
||||||
new_msg = osmo_cc_new_msg(OSMO_CC_MSG_SETUP_COMP_IND);
|
|
||||||
/* send message to osmo-cc */
|
|
||||||
osmo_cc_ll_msg(&ss5->ss5_ep->cc_ep, ss5->dsp.cc_callref, new_msg);
|
|
||||||
}
|
|
||||||
|
|
||||||
/*
|
|
||||||
* dial string generation and parsing
|
|
||||||
*/
|
|
||||||
|
|
||||||
static char *prefix_1_digit[] = { "1", "7", NULL };
|
|
||||||
static char *prefix_2_digit[] = {
|
|
||||||
"20", "27", "28", "30", "31", "32", "33", "34", "36", "39", "40", "41",
|
|
||||||
"43", "44", "45", "46", "47", "48", "49", "51", "52", "53", "54", "55",
|
|
||||||
"56", "57", "58", "60", "61", "62", "63", "64", "65", "66", "81", "82",
|
|
||||||
"83", "84", "86", "89", "90", "91", "92", "93", "94", "95", "98",
|
|
||||||
NULL };
|
|
||||||
|
|
||||||
/* use number and number type to generate an SS5 dial string
|
|
||||||
* the digits are checked if they can be dialed
|
|
||||||
* if the number is already in SS5 format, only digits are checked
|
|
||||||
*/
|
|
||||||
static int generate_dial_string(uint8_t type, const char *dialing, char *string, int string_size)
|
|
||||||
{
|
|
||||||
int full_string_given = 0;
|
|
||||||
int i, ii;
|
|
||||||
|
|
||||||
if ((int)strlen(dialing) + 4 > string_size) {
|
|
||||||
PDEBUG(DSS5, DEBUG_NOTICE, "Dial string is too long for our digit register, call is rejected!\n");
|
|
||||||
return -EINVAL;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* check for correct digits */
|
|
||||||
for (i = 0, ii = strlen(dialing); i < ii; i++) {
|
|
||||||
/* string may start with 'a' or 'b', but then 'c' must be the last digit */
|
|
||||||
if (dialing[i] == 'a' || dialing[i] == 'b') {
|
|
||||||
full_string_given = 1;
|
|
||||||
if (dialing[ii - 1] != 'c') {
|
|
||||||
PDEBUG(DSS5, DEBUG_NOTICE, "Number starts with 'a' (KP1) or 'b' (KP2) but missing 'c' (ST) at the end, call is rejected!\n");
|
|
||||||
return -EINVAL;
|
|
||||||
}
|
|
||||||
/* remove check of last digit 'c' */
|
|
||||||
--ii;
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
/* string must only consist of numerical digits and '*' (code 11) and '#' (code 12) */
|
|
||||||
if (!strchr("0123456789*#", dialing[i])) {
|
|
||||||
PDEBUG(DSS5, DEBUG_NOTICE, "Number has invalid digits, call is rejected!\n");
|
|
||||||
return -EINVAL;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/* if full string with 'a'/'b' and 'c' is given, we have complete dial string */
|
|
||||||
if (full_string_given) {
|
|
||||||
strcpy(string, dialing);
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* if number is not of international type, create national dial string */
|
|
||||||
if (type != OSMO_CC_TYPE_INTERNATIONAL) {
|
|
||||||
// make GCC happy
|
|
||||||
strcpy(string, "a0");
|
|
||||||
strcat(string, dialing);
|
|
||||||
strcat(string, "c");
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* check international prefix with length of 1 digit */
|
|
||||||
if ((int)strlen(dialing) < 1) {
|
|
||||||
PDEBUG(DSS5, DEBUG_NOTICE, "International number too short to get country code from, call is rejected!\n");
|
|
||||||
return -EINVAL;
|
|
||||||
}
|
|
||||||
for (i = 0; prefix_1_digit[i]; i++) {
|
|
||||||
if (prefix_1_digit[i][0] == dialing[0])
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
/* if number is of international type, create international dial string */
|
|
||||||
if (prefix_1_digit[i]) {
|
|
||||||
#pragma GCC diagnostic push
|
|
||||||
#pragma GCC diagnostic ignored "-Wformat-overflow"
|
|
||||||
sprintf(string, "b%c0%sc", dialing[0], dialing + 1);
|
|
||||||
#pragma GCC diagnostic pop
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* check international prefix with length of 2 digits */
|
|
||||||
if ((int)strlen(dialing) < 2) {
|
|
||||||
PDEBUG(DSS5, DEBUG_NOTICE, "International number too short to get country code from, call is rejected!\n");
|
|
||||||
return -EINVAL;
|
|
||||||
}
|
|
||||||
for (i = 0; prefix_2_digit[i]; i++) {
|
|
||||||
if (prefix_2_digit[i][0] == dialing[0]
|
|
||||||
&& prefix_2_digit[i][1] == dialing[1])
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
/* if number is of international type, create international dial string */
|
|
||||||
if (prefix_2_digit[i]) {
|
|
||||||
#pragma GCC diagnostic push
|
|
||||||
#pragma GCC diagnostic ignored "-Wformat-overflow"
|
|
||||||
sprintf(string, "b%c%c0%sc", dialing[0], dialing[1], dialing + 2);
|
|
||||||
#pragma GCC diagnostic pop
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* check international prefix with length of 3 digits */
|
|
||||||
if ((int)strlen(dialing) < 3) {
|
|
||||||
PDEBUG(DSS5, DEBUG_NOTICE, "International number too short to get country code from, call is rejected!\n");
|
|
||||||
return -EINVAL;
|
|
||||||
}
|
|
||||||
/* if number is of international type, create international dial string */
|
|
||||||
#pragma GCC diagnostic push
|
|
||||||
#pragma GCC diagnostic ignored "-Wformat-overflow"
|
|
||||||
sprintf(string, "b%c%c%c0%sc", dialing[0], dialing[1], dialing[2], dialing + 3);
|
|
||||||
#pragma GCC diagnostic pop
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* parse received SS5 dial string and convert it into a national or international number */
|
|
||||||
static int parse_dial_string(uint8_t *type, char *dialing, int dialing_size, const char *string)
|
|
||||||
{
|
|
||||||
char kp_digit;
|
|
||||||
const char *prefix;
|
|
||||||
int length;
|
|
||||||
int i;
|
|
||||||
|
|
||||||
/* remove start and stop digits, set string after start digit and set length to digits between start and stop */
|
|
||||||
if (string[0] != 'a' && string[0] != 'b') {
|
|
||||||
PDEBUG(DSS5, DEBUG_NOTICE, "Received digits do not start with 'a' (KP1) nor 'b' (KP2), call is rejected!\n");
|
|
||||||
return -EINVAL;
|
|
||||||
}
|
|
||||||
kp_digit = *string++;
|
|
||||||
length = strlen(string) - 1;
|
|
||||||
if (string[length] != 'c') {
|
|
||||||
PDEBUG(DSS5, DEBUG_NOTICE, "Received digits do end with 'c' (ST), call is rejected!\n");
|
|
||||||
return -EINVAL;
|
|
||||||
}
|
|
||||||
if (length > dialing_size - 1) {
|
|
||||||
PDEBUG(DSS5, DEBUG_NOTICE, "Received dial string is too long, call is rejected!\n");
|
|
||||||
return -EINVAL;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* received national call */
|
|
||||||
if (kp_digit == 'a') {
|
|
||||||
/* remove discriminaing digit */
|
|
||||||
string++;
|
|
||||||
--length;
|
|
||||||
*type = OSMO_CC_TYPE_NATIONAL;
|
|
||||||
strncpy(dialing, string, length);
|
|
||||||
dialing[length] = '\0';
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* check international prefix with length of 1 digit */
|
|
||||||
if (length < 2) {
|
|
||||||
PDEBUG(DSS5, DEBUG_NOTICE, "International number too short to get country code from, call is rejected!\n");
|
|
||||||
return -EINVAL;
|
|
||||||
}
|
|
||||||
for (i = 0; prefix_1_digit[i]; i++) {
|
|
||||||
if (prefix_1_digit[i][0] == string[0])
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
/* if number is of international type, create international dial string */
|
|
||||||
if (prefix_1_digit[i]) {
|
|
||||||
prefix = string;
|
|
||||||
string += 1;
|
|
||||||
length -= 1;
|
|
||||||
/* remove discriminaing digit */
|
|
||||||
string++;
|
|
||||||
--length;
|
|
||||||
*type = OSMO_CC_TYPE_INTERNATIONAL;
|
|
||||||
dialing[0] = prefix[0];
|
|
||||||
strncpy(dialing + 1, string, length);
|
|
||||||
dialing[1 + length] = '\0';
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* check international prefix with length of 2 digits */
|
|
||||||
if (length < 3) {
|
|
||||||
PDEBUG(DSS5, DEBUG_NOTICE, "International number too short to get country code from, call is rejected!\n");
|
|
||||||
return -EINVAL;
|
|
||||||
}
|
|
||||||
for (i = 0; prefix_2_digit[i]; i++) {
|
|
||||||
if (prefix_2_digit[i][0] == string[0]
|
|
||||||
&& prefix_2_digit[i][1] == string[1])
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
/* if number is of international type, create international dial string */
|
|
||||||
if (prefix_2_digit[i]) {
|
|
||||||
prefix = string;
|
|
||||||
string += 2;
|
|
||||||
length -= 2;
|
|
||||||
/* remove discriminaing digit */
|
|
||||||
string++;
|
|
||||||
--length;
|
|
||||||
*type = OSMO_CC_TYPE_INTERNATIONAL;
|
|
||||||
dialing[0] = prefix[0];
|
|
||||||
dialing[1] = prefix[1];
|
|
||||||
strncpy(dialing + 2, string, length);
|
|
||||||
dialing[2 + length] = '\0';
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* check international prefix with length of 3 digits */
|
|
||||||
if (length < 4) {
|
|
||||||
PDEBUG(DSS5, DEBUG_NOTICE, "International number too short to get country code from, call is rejected!\n");
|
|
||||||
return -EINVAL;
|
|
||||||
}
|
|
||||||
/* if number is of international type, create international dial string */
|
|
||||||
prefix = string;
|
|
||||||
string += 3;
|
|
||||||
length -= 3;
|
|
||||||
/* remove discriminaing digit */
|
|
||||||
string++;
|
|
||||||
--length;
|
|
||||||
*type = OSMO_CC_TYPE_INTERNATIONAL;
|
|
||||||
dialing[0] = prefix[0];
|
|
||||||
dialing[1] = prefix[1];
|
|
||||||
dialing[2] = prefix[2];
|
|
||||||
strncpy(dialing + 3, string, length);
|
|
||||||
dialing[3 + length] = '\0';
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* event handling
|
* event handling
|
||||||
*/
|
*/
|
||||||
|
|
||||||
/* function that receives the digit or the cease of it (' ' or different digit) */
|
/* function that receives the signal or the cease of it (' ' or different signal) */
|
||||||
void receive_digit(void *priv, char digit, double dbm)
|
void receive_signal(void *priv, char signal, double dbm)
|
||||||
{
|
{
|
||||||
ss5_t *ss5 = priv;
|
ss5_t *ss5 = priv;
|
||||||
|
const char *state_name = ss5_state_names[ss5->state];
|
||||||
int i;
|
int i;
|
||||||
int rc;
|
int rc;
|
||||||
|
|
||||||
if (digit > ' ')
|
if (signal > ' ')
|
||||||
PDEBUG_CHAN(DSS5, DEBUG_DEBUG, "Received digit '%c' in '%s' state. (%.1f dBm)\n", digit, ss5_state_names[ss5->state], dbm);
|
PDEBUG_CHAN(DSS5, DEBUG_DEBUG, "Received signal '%c' in '%s' state. (%.1f dBm)\n", signal, state_name, dbm);
|
||||||
else
|
else
|
||||||
PDEBUG_CHAN(DSS5, DEBUG_DEBUG, "Received cease of digit in '%s' state.\n", ss5_state_names[ss5->state]);
|
PDEBUG_CHAN(DSS5, DEBUG_DEBUG, "Received cease of signal in '%s' state.\n", state_name);
|
||||||
|
|
||||||
/* a clear forward (not release guard) at any state (including idle state) */
|
/* a clear forward (not release guard) at any state (including idle state) */
|
||||||
if (ss5->state != SS5_STATE_SEND_CLR_FWD && digit == 'C') {
|
if (ss5->state != SS5_STATE_SEND_CLR_FWD && signal == 'C') {
|
||||||
PDEBUG_CHAN(DSS5, DEBUG_INFO, "Received 'clear-forward' signal in '%s' state, sending 'release-guard' and clearing call.\n", ss5_state_names[ss5->state]);
|
PDEBUG_CHAN(DSS5, DEBUG_INFO, "Received 'clear-forward' signal in '%s' state, sending 'release-guard' and clearing call.\n", state_name);
|
||||||
/* release outgoing call */
|
/* release outgoing call */
|
||||||
if (ss5->dsp.cc_callref) {
|
if (ss5->dsp.cc_callref) {
|
||||||
/* send release indication towards CC */
|
/* send release indication towards CC */
|
||||||
release_call(ss5, OSMO_CC_ISDN_CAUSE_NORM_CALL_CLEAR);
|
release_call(&ss5->ss5_ep->cc_ep, ss5->dsp.cc_callref, OSMO_CC_ISDN_CAUSE_NORM_CALL_CLEAR);
|
||||||
/* remove ref */
|
/* remove ref */
|
||||||
ss5->dsp.cc_callref = 0;
|
ss5->dsp.cc_callref = 0;
|
||||||
}
|
}
|
||||||
|
@ -594,7 +296,7 @@ void receive_digit(void *priv, char digit, double dbm)
|
||||||
/* change state */
|
/* change state */
|
||||||
ss5_new_state(ss5, SS5_STATE_SEND_RELEASE);
|
ss5_new_state(ss5, SS5_STATE_SEND_RELEASE);
|
||||||
/* unset dial string, if set */
|
/* unset dial string, if set */
|
||||||
set_dial_string(&ss5->dsp, "");
|
set_dial_string(&ss5->dsp, "", 0);
|
||||||
/* send release-guard */
|
/* send release-guard */
|
||||||
set_tone(&ss5->dsp, 'C', 0);
|
set_tone(&ss5->dsp, 'C', 0);
|
||||||
/* to prevent blueboxing */
|
/* to prevent blueboxing */
|
||||||
|
@ -609,15 +311,15 @@ void receive_digit(void *priv, char digit, double dbm)
|
||||||
switch (ss5->state) {
|
switch (ss5->state) {
|
||||||
/* release guard */
|
/* release guard */
|
||||||
case SS5_STATE_SEND_RELEASE:
|
case SS5_STATE_SEND_RELEASE:
|
||||||
if (digit != 'C') {
|
if (signal != 'C') {
|
||||||
/* wait at least the minimum release-guard time, to prevent blueboxing */
|
/* wait at least the minimum release-guard time, to prevent blueboxing */
|
||||||
if (timer_running(&ss5->timer)) {
|
if (timer_running(&ss5->timer)) {
|
||||||
PDEBUG_CHAN(DSS5, DEBUG_INFO, "'clear-forward' is ceased in '%s' state, must wait to prevent blueboxing.\n", ss5_state_names[ss5->state]);
|
PDEBUG_CHAN(DSS5, DEBUG_INFO, "'clear-forward' is ceased in '%s' state, must wait to prevent blueboxing.\n", state_name);
|
||||||
/* state idle */
|
/* state idle */
|
||||||
ss5_new_state(ss5, SS5_STATE_SEND_REL_WAIT);
|
ss5_new_state(ss5, SS5_STATE_SEND_REL_WAIT);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
PDEBUG_CHAN(DSS5, DEBUG_INFO, "'clear-forward' is ceased in '%s' state, going idle.\n", ss5_state_names[ss5->state]);
|
PDEBUG_CHAN(DSS5, DEBUG_INFO, "'clear-forward' is ceased in '%s' state, going idle.\n", state_name);
|
||||||
/* cease */
|
/* cease */
|
||||||
set_tone(&ss5->dsp, 0, 0);
|
set_tone(&ss5->dsp, 0, 0);
|
||||||
/* state idle */
|
/* state idle */
|
||||||
|
@ -628,15 +330,15 @@ void receive_digit(void *priv, char digit, double dbm)
|
||||||
break;
|
break;
|
||||||
/* outgoing call sends seize */
|
/* outgoing call sends seize */
|
||||||
case SS5_STATE_SEND_SEIZE:
|
case SS5_STATE_SEND_SEIZE:
|
||||||
if (digit == 'A') {
|
if (signal == 'A') {
|
||||||
PDEBUG_CHAN(DSS5, DEBUG_INFO, "Received 'seize' signal in '%s' state, this is double seizure.\n", ss5_state_names[ss5->state]);
|
PDEBUG_CHAN(DSS5, DEBUG_INFO, "Received 'seize' signal in '%s' state, this is double seizure.\n", state_name);
|
||||||
/* set timeout */
|
/* set timeout */
|
||||||
timer_start(&ss5->timer, DUR_DOUBLE_SEIZURE);
|
timer_start(&ss5->timer, DUR_DOUBLE_SEIZURE);
|
||||||
/* change state */
|
/* change state */
|
||||||
ss5_new_state(ss5, SS5_STATE_DOUBLE_SEIZE);
|
ss5_new_state(ss5, SS5_STATE_DOUBLE_SEIZE);
|
||||||
}
|
}
|
||||||
if (digit == 'B') {
|
if (signal == 'B') {
|
||||||
PDEBUG_CHAN(DSS5, DEBUG_INFO, "Received 'proceed-to-send' signal in '%s' state, ceasing 'seize' signal.\n", ss5_state_names[ss5->state]);
|
PDEBUG_CHAN(DSS5, DEBUG_INFO, "Received 'proceed-to-send' signal in '%s' state, ceasing 'seize' signal.\n", state_name);
|
||||||
/* set recognition time to normal */
|
/* set recognition time to normal */
|
||||||
set_sig_detect_duration(&ss5->dsp, SIGN_RECOGNITION_NORMAL, SIGN_RECOGNITION_NORMAL);
|
set_sig_detect_duration(&ss5->dsp, SIGN_RECOGNITION_NORMAL, SIGN_RECOGNITION_NORMAL);
|
||||||
/* cease */
|
/* cease */
|
||||||
|
@ -652,63 +354,67 @@ void receive_digit(void *priv, char digit, double dbm)
|
||||||
break;
|
break;
|
||||||
/* outgoing call receives proceed-to-send */
|
/* outgoing call receives proceed-to-send */
|
||||||
case SS5_STATE_RECV_PROCEED:
|
case SS5_STATE_RECV_PROCEED:
|
||||||
if (digit != 'B') {
|
if (signal != 'B') {
|
||||||
PDEBUG_CHAN(DSS5, DEBUG_INFO, "proceed-to-send' is ceased in '%s' state, sendig digits.\n", ss5_state_names[ss5->state]);
|
PDEBUG_CHAN(DSS5, DEBUG_INFO, "proceed-to-send' is ceased in '%s' state, sendig digits.\n", state_name);
|
||||||
/* cease */
|
/* cease */
|
||||||
set_tone(&ss5->dsp, 0, 0);
|
set_tone(&ss5->dsp, 0, 0);
|
||||||
/* dial */
|
/* dial */
|
||||||
set_tone(&ss5->dsp, ' ', PAUSE_BEFORE_DIALING);
|
set_tone(&ss5->dsp, ' ', PAUSE_BEFORE_DIALING);
|
||||||
set_dial_string(&ss5->dsp, ss5->dialing);
|
set_dial_string(&ss5->dsp, ss5->dialing, 0);
|
||||||
/* change state */
|
/* change state */
|
||||||
ss5_new_state(ss5, SS5_STATE_SEND_DIGITS);
|
ss5_new_state(ss5, SS5_STATE_SEND_DIGITS);
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
/* outgoing call receives answer or busy-flash */
|
/* outgoing call receives answer or busy-flash */
|
||||||
case SS5_STATE_OUT_INACTIVE:
|
case SS5_STATE_OUT_INACTIVE:
|
||||||
if (digit == 'A') {
|
if (signal == 'A') {
|
||||||
PDEBUG_CHAN(DSS5, DEBUG_INFO, "Received 'answer' signal in '%s' state, sending 'acknowledge'.\n", ss5_state_names[ss5->state]);
|
PDEBUG_CHAN(DSS5, DEBUG_INFO, "Received 'answer' signal in '%s' state, sending 'acknowledge'.\n", state_name);
|
||||||
/* send acknowledge */
|
/* send acknowledge */
|
||||||
set_tone(&ss5->dsp, 'A', MAX_ACKNOWLEDGE);
|
set_tone(&ss5->dsp, 'A', MAX_ACKNOWLEDGE);
|
||||||
/* change state */
|
/* change state */
|
||||||
ss5_new_state(ss5, SS5_STATE_SEND_ACK_ANS);
|
ss5_new_state(ss5, SS5_STATE_SEND_ACK_ANS);
|
||||||
/* indicate answer to upper layer */
|
/* indicate answer to upper layer */
|
||||||
answer_call(ss5);
|
answer_call(&ss5->ss5_ep->cc_ep, ss5->dsp.cc_callref);
|
||||||
}
|
}
|
||||||
if (digit == 'B') {
|
if (signal == 'B') {
|
||||||
PDEBUG_CHAN(DSS5, DEBUG_INFO, "Received 'busy-flash' signal in '%s' state, sending 'acknowledge'.\n", ss5_state_names[ss5->state]);
|
PDEBUG_CHAN(DSS5, DEBUG_INFO, "Received 'busy-flash' signal in '%s' state, sending 'acknowledge'.\n", state_name);
|
||||||
/* send acknowledge */
|
/* send acknowledge */
|
||||||
set_tone(&ss5->dsp, 'A', MAX_ACKNOWLEDGE);
|
set_tone(&ss5->dsp, 'A', MAX_ACKNOWLEDGE);
|
||||||
/* change state */
|
/* change state */
|
||||||
ss5_new_state(ss5, SS5_STATE_SEND_ACK_BUS);
|
ss5_new_state(ss5, SS5_STATE_SEND_ACK_BUS);
|
||||||
|
if (!ss5->ss5_ep->suppress_disconnect) {
|
||||||
/* indicate disconnect w/tones to upper layer */
|
/* indicate disconnect w/tones to upper layer */
|
||||||
disconnect_call(ss5, OSMO_CC_ISDN_CAUSE_USER_BUSY);
|
disconnect_call(&ss5->ss5_ep->cc_ep, ss5->dsp.cc_callref, OSMO_CC_ISDN_CAUSE_USER_BUSY);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
/* outgoing call receives clear-back */
|
/* outgoing call receives clear-back */
|
||||||
case SS5_STATE_OUT_ACTIVE:
|
case SS5_STATE_OUT_ACTIVE:
|
||||||
if (digit == 'A') {
|
if (signal == 'A') {
|
||||||
PDEBUG_CHAN(DSS5, DEBUG_INFO, "Received 'answer' signal in '%s' state, sending 'acknowledge'.\n", ss5_state_names[ss5->state]);
|
PDEBUG_CHAN(DSS5, DEBUG_INFO, "Received 'answer' signal in '%s' state, sending 'acknowledge'.\n", state_name);
|
||||||
/* send acknowledge */
|
/* send acknowledge */
|
||||||
set_tone(&ss5->dsp, 'A', MAX_ACKNOWLEDGE);
|
set_tone(&ss5->dsp, 'A', MAX_ACKNOWLEDGE);
|
||||||
/* change state */
|
/* change state */
|
||||||
ss5_new_state(ss5, SS5_STATE_SEND_ACK_ANS);
|
ss5_new_state(ss5, SS5_STATE_SEND_ACK_ANS);
|
||||||
/* indicate answer to upper layer */
|
/* indicate answer to upper layer */
|
||||||
answer_call(ss5);
|
answer_call(&ss5->ss5_ep->cc_ep, ss5->dsp.cc_callref);
|
||||||
}
|
}
|
||||||
if (digit == 'B') {
|
if (signal == 'B') {
|
||||||
PDEBUG_CHAN(DSS5, DEBUG_INFO, "Received 'clear-back' signal in '%s' state, sending 'acknowledge'.\n", ss5_state_names[ss5->state]);
|
PDEBUG_CHAN(DSS5, DEBUG_INFO, "Received 'clear-back' signal in '%s' state, sending 'acknowledge'.\n", state_name);
|
||||||
/* send acknowledge */
|
/* send acknowledge */
|
||||||
set_tone(&ss5->dsp, 'A', MAX_ACKNOWLEDGE);
|
set_tone(&ss5->dsp, 'A', MAX_ACKNOWLEDGE);
|
||||||
/* change state */
|
/* change state */
|
||||||
ss5_new_state(ss5, SS5_STATE_SEND_ACK_CLR);
|
ss5_new_state(ss5, SS5_STATE_SEND_ACK_CLR);
|
||||||
|
if (!ss5->ss5_ep->suppress_disconnect) {
|
||||||
/* indicate disconnect w/tones to upper layer */
|
/* indicate disconnect w/tones to upper layer */
|
||||||
disconnect_call(ss5, OSMO_CC_ISDN_CAUSE_NORM_CALL_CLEAR);
|
disconnect_call(&ss5->ss5_ep->cc_ep, ss5->dsp.cc_callref, OSMO_CC_ISDN_CAUSE_NORM_CALL_CLEAR);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
/* outgoing call receives answer */
|
/* outgoing call receives answer */
|
||||||
case SS5_STATE_SEND_ACK_ANS:
|
case SS5_STATE_SEND_ACK_ANS:
|
||||||
if (digit != 'A') {
|
if (signal != 'A') {
|
||||||
PDEBUG_CHAN(DSS5, DEBUG_INFO, "'answer' is ceased in '%s' state, call is established.\n", ss5_state_names[ss5->state]);
|
PDEBUG_CHAN(DSS5, DEBUG_INFO, "'answer' is ceased in '%s' state, call is established.\n", state_name);
|
||||||
/* stop timer */
|
/* stop timer */
|
||||||
timer_stop(&ss5->timer);
|
timer_stop(&ss5->timer);
|
||||||
/* cease */
|
/* cease */
|
||||||
|
@ -720,12 +426,12 @@ void receive_digit(void *priv, char digit, double dbm)
|
||||||
/* outgoing call receives busy-flash */
|
/* outgoing call receives busy-flash */
|
||||||
case SS5_STATE_SEND_ACK_BUS:
|
case SS5_STATE_SEND_ACK_BUS:
|
||||||
case SS5_STATE_SEND_DIGITS:
|
case SS5_STATE_SEND_DIGITS:
|
||||||
if (digit != 'B') {
|
if (signal != 'B') {
|
||||||
PDEBUG_CHAN(DSS5, DEBUG_INFO, "'busy-flash' is ceased in '%s' state, call is disconnected.\n", ss5_state_names[ss5->state]);
|
PDEBUG_CHAN(DSS5, DEBUG_INFO, "'busy-flash' is ceased in '%s' state, call is disconnected.\n", state_name);
|
||||||
/* stop timer */
|
/* stop timer */
|
||||||
timer_stop(&ss5->timer);
|
timer_stop(&ss5->timer);
|
||||||
/* unset dial string, if set */
|
/* unset dial string, if set */
|
||||||
set_dial_string(&ss5->dsp, "");
|
set_dial_string(&ss5->dsp, "", 0);
|
||||||
/* cease */
|
/* cease */
|
||||||
set_tone(&ss5->dsp, 0, 0);
|
set_tone(&ss5->dsp, 0, 0);
|
||||||
/* change state */
|
/* change state */
|
||||||
|
@ -734,8 +440,8 @@ void receive_digit(void *priv, char digit, double dbm)
|
||||||
break;
|
break;
|
||||||
/* outgoing call receives clear-back */
|
/* outgoing call receives clear-back */
|
||||||
case SS5_STATE_SEND_ACK_CLR:
|
case SS5_STATE_SEND_ACK_CLR:
|
||||||
if (digit != 'B') {
|
if (signal != 'B') {
|
||||||
PDEBUG_CHAN(DSS5, DEBUG_INFO, "'clear-back' is ceased in '%s' state, call is disconnected.\n", ss5_state_names[ss5->state]);
|
PDEBUG_CHAN(DSS5, DEBUG_INFO, "'clear-back' is ceased in '%s' state, call is disconnected.\n", state_name);
|
||||||
/* stop timer */
|
/* stop timer */
|
||||||
timer_stop(&ss5->timer);
|
timer_stop(&ss5->timer);
|
||||||
/* cease */
|
/* cease */
|
||||||
|
@ -746,8 +452,8 @@ void receive_digit(void *priv, char digit, double dbm)
|
||||||
break;
|
break;
|
||||||
/* outgoing call sends clear forward */
|
/* outgoing call sends clear forward */
|
||||||
case SS5_STATE_SEND_CLR_FWD:
|
case SS5_STATE_SEND_CLR_FWD:
|
||||||
if (digit == 'C') {
|
if (signal == 'C') {
|
||||||
PDEBUG_CHAN(DSS5, DEBUG_INFO, "Received 'release-guard' signal in '%s' state, ceasing 'clear-forward' signal.\n", ss5_state_names[ss5->state]);
|
PDEBUG_CHAN(DSS5, DEBUG_INFO, "Received 'release-guard' signal in '%s' state, ceasing 'clear-forward' signal.\n", state_name);
|
||||||
/* cease */
|
/* cease */
|
||||||
set_tone(&ss5->dsp, 0, 0);
|
set_tone(&ss5->dsp, 0, 0);
|
||||||
/* stop timer */
|
/* stop timer */
|
||||||
|
@ -758,8 +464,8 @@ void receive_digit(void *priv, char digit, double dbm)
|
||||||
break;
|
break;
|
||||||
/* outgoing call receives release guard */
|
/* outgoing call receives release guard */
|
||||||
case SS5_STATE_RECV_RELEASE:
|
case SS5_STATE_RECV_RELEASE:
|
||||||
if (digit != 'C') {
|
if (signal != 'C') {
|
||||||
PDEBUG_CHAN(DSS5, DEBUG_INFO, "'release-guard' is ceased in '%s' state, going idle.\n", ss5_state_names[ss5->state]);
|
PDEBUG_CHAN(DSS5, DEBUG_INFO, "'release-guard' is ceased in '%s' state, going idle.\n", state_name);
|
||||||
/* state idle */
|
/* state idle */
|
||||||
ss5_new_state(ss5, SS5_STATE_IDLE);
|
ss5_new_state(ss5, SS5_STATE_IDLE);
|
||||||
/* reset instance */
|
/* reset instance */
|
||||||
|
@ -768,8 +474,8 @@ void receive_digit(void *priv, char digit, double dbm)
|
||||||
break;
|
break;
|
||||||
/* incoming call receives seize */
|
/* incoming call receives seize */
|
||||||
case SS5_STATE_IDLE:
|
case SS5_STATE_IDLE:
|
||||||
if (digit == 'A') {
|
if (signal == 'A') {
|
||||||
PDEBUG_CHAN(DSS5, DEBUG_INFO, "Received 'seize' signal in '%s' state, sending 'proceed-to-send'.\n", ss5_state_names[ss5->state]);
|
PDEBUG_CHAN(DSS5, DEBUG_INFO, "Received 'seize' signal in '%s' state, sending 'proceed-to-send'.\n", state_name);
|
||||||
/* set recognition time to normal */
|
/* set recognition time to normal */
|
||||||
set_sig_detect_duration(&ss5->dsp, SIGN_RECOGNITION_NORMAL, SIGN_RECOGNITION_NORMAL);
|
set_sig_detect_duration(&ss5->dsp, SIGN_RECOGNITION_NORMAL, SIGN_RECOGNITION_NORMAL);
|
||||||
/* change state */
|
/* change state */
|
||||||
|
@ -780,8 +486,8 @@ void receive_digit(void *priv, char digit, double dbm)
|
||||||
break;
|
break;
|
||||||
/* incoming call sends proceed-to-send */
|
/* incoming call sends proceed-to-send */
|
||||||
case SS5_STATE_SEND_PROCEED:
|
case SS5_STATE_SEND_PROCEED:
|
||||||
if (digit != 'A') {
|
if (signal != 'A') {
|
||||||
PDEBUG_CHAN(DSS5, DEBUG_INFO, "'seize' is ceased in '%s' state, receiving digits.\n", ss5_state_names[ss5->state]);
|
PDEBUG_CHAN(DSS5, DEBUG_INFO, "'seize' is ceased in '%s' state, receiving digits.\n", state_name);
|
||||||
/* cease */
|
/* cease */
|
||||||
set_tone(&ss5->dsp, 0, 0);
|
set_tone(&ss5->dsp, 0, 0);
|
||||||
/* change state */
|
/* change state */
|
||||||
|
@ -792,15 +498,15 @@ void receive_digit(void *priv, char digit, double dbm)
|
||||||
break;
|
break;
|
||||||
/* incoming call receives digits */
|
/* incoming call receives digits */
|
||||||
case SS5_STATE_RECV_DIGIT:
|
case SS5_STATE_RECV_DIGIT:
|
||||||
if (!(digit >= '0' && digit <= '9') && !(digit >= 'a' && digit <= 'c')) {
|
if (!(signal >= '0' && signal <= '9') && !(signal >= 'a' && signal <= 'c')) {
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
PDEBUG_CHAN(DSS5, DEBUG_INFO, "Digit '%c' is ceased in '%s' state.\n", digit, ss5_state_names[ss5->state]);
|
PDEBUG_CHAN(DSS5, DEBUG_INFO, "Digit '%c' is detected in '%s' state.\n", signal, state_name);
|
||||||
/* add digit */
|
/* add digit */
|
||||||
i = strlen(ss5->dialing);
|
i = strlen(ss5->dialing);
|
||||||
if (i + 1 == sizeof(ss5->dialing))
|
if (i + 1 == sizeof(ss5->dialing))
|
||||||
break;
|
break;
|
||||||
ss5->dialing[i++] = digit;
|
ss5->dialing[i++] = signal;
|
||||||
ss5->dialing[i] = '\0';
|
ss5->dialing[i] = '\0';
|
||||||
/* change state */
|
/* change state */
|
||||||
ss5_new_state(ss5, SS5_STATE_RECV_SPACE);
|
ss5_new_state(ss5, SS5_STATE_RECV_SPACE);
|
||||||
|
@ -808,7 +514,7 @@ void receive_digit(void *priv, char digit, double dbm)
|
||||||
timer_start(&ss5->timer, TO_DIALING);
|
timer_start(&ss5->timer, TO_DIALING);
|
||||||
break;
|
break;
|
||||||
case SS5_STATE_RECV_SPACE:
|
case SS5_STATE_RECV_SPACE:
|
||||||
if (digit != ' ')
|
if (signal != ' ')
|
||||||
break;
|
break;
|
||||||
/* check for end of dialing */
|
/* check for end of dialing */
|
||||||
i = strlen(ss5->dialing) - 1;
|
i = strlen(ss5->dialing) - 1;
|
||||||
|
@ -859,8 +565,8 @@ void receive_digit(void *priv, char digit, double dbm)
|
||||||
break;
|
break;
|
||||||
/* incoming call sends answer */
|
/* incoming call sends answer */
|
||||||
case SS5_STATE_SEND_ANSWER:
|
case SS5_STATE_SEND_ANSWER:
|
||||||
if (digit == 'A') {
|
if (signal == 'A') {
|
||||||
PDEBUG_CHAN(DSS5, DEBUG_INFO, "Received 'acknowledge' to 'answer' in '%s' state, call is now active.\n", ss5_state_names[ss5->state]);
|
PDEBUG_CHAN(DSS5, DEBUG_INFO, "Received 'acknowledge' to 'answer' in '%s' state, call is now active.\n", state_name);
|
||||||
/* stop timer */
|
/* stop timer */
|
||||||
timer_stop(&ss5->timer);
|
timer_stop(&ss5->timer);
|
||||||
/* cease */
|
/* cease */
|
||||||
|
@ -871,8 +577,8 @@ void receive_digit(void *priv, char digit, double dbm)
|
||||||
break;
|
break;
|
||||||
/* incoming call sends busy-flash */
|
/* incoming call sends busy-flash */
|
||||||
case SS5_STATE_SEND_BUSY:
|
case SS5_STATE_SEND_BUSY:
|
||||||
if (digit == 'A') {
|
if (signal == 'A') {
|
||||||
PDEBUG_CHAN(DSS5, DEBUG_INFO, "Received 'acknowledge' to 'busy-flash' in '%s' state, call is now inactive.\n", ss5_state_names[ss5->state]);
|
PDEBUG_CHAN(DSS5, DEBUG_INFO, "Received 'acknowledge' to 'busy-flash' in '%s' state, call is now inactive.\n", state_name);
|
||||||
/* stop timer */
|
/* stop timer */
|
||||||
timer_stop(&ss5->timer);
|
timer_stop(&ss5->timer);
|
||||||
/* cease */
|
/* cease */
|
||||||
|
@ -883,8 +589,8 @@ void receive_digit(void *priv, char digit, double dbm)
|
||||||
break;
|
break;
|
||||||
/* incoming call sends clear-back */
|
/* incoming call sends clear-back */
|
||||||
case SS5_STATE_SEND_CLR_BAK:
|
case SS5_STATE_SEND_CLR_BAK:
|
||||||
if (digit == 'A') {
|
if (signal == 'A') {
|
||||||
PDEBUG_CHAN(DSS5, DEBUG_INFO, "Received 'acknowledge' to 'clear-back' in '%s' state, call is now inactive.\n", ss5_state_names[ss5->state]);
|
PDEBUG_CHAN(DSS5, DEBUG_INFO, "Received 'acknowledge' to 'clear-back' in '%s' state, call is now inactive.\n", state_name);
|
||||||
/* stop timer */
|
/* stop timer */
|
||||||
timer_stop(&ss5->timer);
|
timer_stop(&ss5->timer);
|
||||||
/* cease */
|
/* cease */
|
||||||
|
@ -894,7 +600,7 @@ void receive_digit(void *priv, char digit, double dbm)
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
default:
|
default:
|
||||||
PDEBUG_CHAN(DSS5, DEBUG_ERROR, "Received digit '%c' in '%s' state is not handled, please fix!\n", digit, ss5_state_names[ss5->state]);
|
PDEBUG_CHAN(DSS5, DEBUG_NOTICE, "Received signal '%c' in '%s' state is not handled!\n", signal, state_name);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -910,18 +616,19 @@ void dialing_complete(void *priv)
|
||||||
}
|
}
|
||||||
|
|
||||||
/* indicate alerting */
|
/* indicate alerting */
|
||||||
alert_call(ss5);
|
alert_call(&ss5->ss5_ep->cc_ep, ss5->dsp.cc_callref);
|
||||||
}
|
}
|
||||||
|
|
||||||
/* timeouts */
|
/* timeouts */
|
||||||
static void ss5_timeout(void *data)
|
static void ss5_timeout(void *data)
|
||||||
{
|
{
|
||||||
ss5_t *ss5 = data;
|
ss5_t *ss5 = data;
|
||||||
|
const char *state_name = ss5_state_names[ss5->state];
|
||||||
|
|
||||||
switch (ss5->state) {
|
switch (ss5->state) {
|
||||||
case SS5_STATE_RECV_DIGIT:
|
case SS5_STATE_RECV_DIGIT:
|
||||||
case SS5_STATE_RECV_SPACE:
|
case SS5_STATE_RECV_SPACE:
|
||||||
PDEBUG_CHAN(DSS5, DEBUG_INFO, "Received timeout in '%s' state, sending 'clear-back'.\n", ss5_state_names[ss5->state]);
|
PDEBUG_CHAN(DSS5, DEBUG_INFO, "Received timeout in '%s' state, sending 'clear-back'.\n", state_name);
|
||||||
/* send clear-back */
|
/* send clear-back */
|
||||||
set_tone(&ss5->dsp, 'B', MAX_CLEAR_BACK);
|
set_tone(&ss5->dsp, 'B', MAX_CLEAR_BACK);
|
||||||
/* change state */
|
/* change state */
|
||||||
|
@ -933,7 +640,7 @@ static void ss5_timeout(void *data)
|
||||||
/* state idle */
|
/* state idle */
|
||||||
ss5_new_state(ss5, SS5_STATE_IDLE);
|
ss5_new_state(ss5, SS5_STATE_IDLE);
|
||||||
/* send release indication towards CC */
|
/* send release indication towards CC */
|
||||||
release_call(ss5, OSMO_CC_ISDN_CAUSE_NO_CIRCUIT_CHAN);
|
release_call(&ss5->ss5_ep->cc_ep, ss5->dsp.cc_callref, OSMO_CC_ISDN_CAUSE_NO_CIRCUIT_CHAN);
|
||||||
/* reset inst */
|
/* reset inst */
|
||||||
link_reset(ss5);
|
link_reset(ss5);
|
||||||
break;
|
break;
|
||||||
|
@ -957,6 +664,7 @@ void cc_message(osmo_cc_endpoint_t *ep, uint32_t callref, osmo_cc_msg_t *msg)
|
||||||
ss5_endpoint_t *ss5_ep = ep->priv;
|
ss5_endpoint_t *ss5_ep = ep->priv;
|
||||||
dsp_t *dsp;
|
dsp_t *dsp;
|
||||||
ss5_t *ss5;
|
ss5_t *ss5;
|
||||||
|
const char *state_name;
|
||||||
osmo_cc_msg_t *new_msg;
|
osmo_cc_msg_t *new_msg;
|
||||||
uint8_t type, plan, present, screen;
|
uint8_t type, plan, present, screen;
|
||||||
char dialing[64];
|
char dialing[64];
|
||||||
|
@ -989,16 +697,17 @@ void cc_message(osmo_cc_endpoint_t *ep, uint32_t callref, osmo_cc_msg_t *msg)
|
||||||
ss5 = (dsp) ? dsp->priv : NULL;
|
ss5 = (dsp) ? dsp->priv : NULL;
|
||||||
if (!ss5) {
|
if (!ss5) {
|
||||||
PDEBUG(DSS5, DEBUG_NOTICE, "No free ss5 instance, rejecting.\n");
|
PDEBUG(DSS5, DEBUG_NOTICE, "No free ss5 instance, rejecting.\n");
|
||||||
reject_call(ss5_ep, callref, OSMO_CC_ISDN_CAUSE_NO_CIRCUIT_CHAN);
|
reject_call(&ss5_ep->cc_ep, callref, OSMO_CC_ISDN_CAUSE_NO_CIRCUIT_CHAN);
|
||||||
goto done;
|
goto done;
|
||||||
}
|
}
|
||||||
/* link with cc */
|
/* link with cc */
|
||||||
ss5->dsp.cc_callref = callref;
|
ss5->dsp.cc_callref = callref;
|
||||||
}
|
}
|
||||||
|
state_name = ss5_state_names[ss5->state];
|
||||||
|
|
||||||
switch (msg->type) {
|
switch (msg->type) {
|
||||||
case OSMO_CC_MSG_SETUP_REQ: /* dial-out command received from epoint */
|
case OSMO_CC_MSG_SETUP_REQ: /* dial-out command received from epoint */
|
||||||
PDEBUG_CHAN(DSS5, DEBUG_INFO, "Outgoing call in '%s' state, sending 'seize'.\n", ss5_state_names[ss5->state]);
|
PDEBUG_CHAN(DSS5, DEBUG_INFO, "Outgoing call in '%s' state, sending 'seize'.\n", state_name);
|
||||||
/* caller id */
|
/* caller id */
|
||||||
rc = osmo_cc_get_ie_calling(msg, 0, &type, &plan, &present, &screen, ss5->callerid, sizeof(ss5->callerid));
|
rc = osmo_cc_get_ie_calling(msg, 0, &type, &plan, &present, &screen, ss5->callerid, sizeof(ss5->callerid));
|
||||||
/* called number */
|
/* called number */
|
||||||
|
@ -1006,7 +715,7 @@ void cc_message(osmo_cc_endpoint_t *ep, uint32_t callref, osmo_cc_msg_t *msg)
|
||||||
if (rc < 0 || !dialing[0]) {
|
if (rc < 0 || !dialing[0]) {
|
||||||
PDEBUG_CHAN(DSS5, DEBUG_NOTICE, "No number given, call is rejected!\n");
|
PDEBUG_CHAN(DSS5, DEBUG_NOTICE, "No number given, call is rejected!\n");
|
||||||
inv_nr:
|
inv_nr:
|
||||||
reject_call(ss5->ss5_ep, ss5->dsp.cc_callref, OSMO_CC_ISDN_CAUSE_INV_NR_FORMAT);
|
reject_call(&ss5->ss5_ep->cc_ep, ss5->dsp.cc_callref, OSMO_CC_ISDN_CAUSE_INV_NR_FORMAT);
|
||||||
link_reset(ss5);
|
link_reset(ss5);
|
||||||
goto done;
|
goto done;
|
||||||
}
|
}
|
||||||
|
@ -1016,12 +725,12 @@ void cc_message(osmo_cc_endpoint_t *ep, uint32_t callref, osmo_cc_msg_t *msg)
|
||||||
/* sdp accept */
|
/* sdp accept */
|
||||||
sdp = osmo_cc_helper_audio_accept(&ss5->ss5_ep->cc_ep.session_config, &ss5->dsp, codecs, down_audio, msg, &ss5->dsp.cc_session, &ss5->dsp.codec, 0);
|
sdp = osmo_cc_helper_audio_accept(&ss5->ss5_ep->cc_ep.session_config, &ss5->dsp, codecs, down_audio, msg, &ss5->dsp.cc_session, &ss5->dsp.codec, 0);
|
||||||
if (!sdp) {
|
if (!sdp) {
|
||||||
reject_call(ss5->ss5_ep, ss5->dsp.cc_callref, OSMO_CC_ISDN_CAUSE_RESOURCE_UNAVAIL);
|
reject_call(&ss5->ss5_ep->cc_ep, ss5->dsp.cc_callref, OSMO_CC_ISDN_CAUSE_RESOURCE_UNAVAIL);
|
||||||
link_reset(ss5);
|
link_reset(ss5);
|
||||||
goto done;
|
goto done;
|
||||||
}
|
}
|
||||||
/* proceed */
|
/* proceed */
|
||||||
proceed_call(ss5, sdp);
|
proceed_call(&ss5->ss5_ep->cc_ep, ss5->dsp.cc_callref, sdp);
|
||||||
/* send seize */
|
/* send seize */
|
||||||
set_tone(&ss5->dsp, 'A', MAX_SEIZE);
|
set_tone(&ss5->dsp, 'A', MAX_SEIZE);
|
||||||
/* change state */
|
/* change state */
|
||||||
|
@ -1040,19 +749,19 @@ void cc_message(osmo_cc_endpoint_t *ep, uint32_t callref, osmo_cc_msg_t *msg)
|
||||||
/* change state */
|
/* change state */
|
||||||
ss5_new_state(ss5, SS5_STATE_SEND_BUSY);
|
ss5_new_state(ss5, SS5_STATE_SEND_BUSY);
|
||||||
/* release call */
|
/* release call */
|
||||||
release_call(ss5, OSMO_CC_ISDN_CAUSE_RESOURCE_UNAVAIL);
|
release_call(&ss5->ss5_ep->cc_ep, ss5->dsp.cc_callref, OSMO_CC_ISDN_CAUSE_RESOURCE_UNAVAIL);
|
||||||
/* reset inst */
|
/* reset inst */
|
||||||
link_reset(ss5);
|
link_reset(ss5);
|
||||||
goto done;
|
goto done;
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
case OSMO_CC_MSG_SETUP_RSP: /* call of endpoint is connected */
|
case OSMO_CC_MSG_SETUP_RSP: /* call of endpoint is connected */
|
||||||
PDEBUG_CHAN(DSS5, DEBUG_INFO, "Incoming call has answered in '%s' state, sending 'answer'.\n", ss5_state_names[ss5->state]);
|
PDEBUG_CHAN(DSS5, DEBUG_INFO, "Incoming call has answered in '%s' state, sending 'answer'.\n", state_name);
|
||||||
rc = osmo_cc_helper_audio_negotiate(msg, &ss5->dsp.cc_session, &ss5->dsp.codec);
|
rc = osmo_cc_helper_audio_negotiate(msg, &ss5->dsp.cc_session, &ss5->dsp.codec);
|
||||||
if (rc < 0)
|
if (rc < 0)
|
||||||
goto codec_failed;
|
goto codec_failed;
|
||||||
/* connect acknowledge */
|
/* connect acknowledge */
|
||||||
setup_comp_call(ss5);
|
setup_comp_call(&ss5->ss5_ep->cc_ep, ss5->dsp.cc_callref);
|
||||||
/* not in right state, which should never happen anyway */
|
/* not in right state, which should never happen anyway */
|
||||||
if (ss5->state != SS5_STATE_IN_INACTIVE)
|
if (ss5->state != SS5_STATE_IN_INACTIVE)
|
||||||
break;
|
break;
|
||||||
|
@ -1069,20 +778,20 @@ void cc_message(osmo_cc_endpoint_t *ep, uint32_t callref, osmo_cc_msg_t *msg)
|
||||||
goto codec_failed;
|
goto codec_failed;
|
||||||
/* right state */
|
/* right state */
|
||||||
if (ss5->state == SS5_STATE_IN_INACTIVE) {
|
if (ss5->state == SS5_STATE_IN_INACTIVE) {
|
||||||
PDEBUG_CHAN(DSS5, DEBUG_INFO, "Incoming call has disconnected in '%s' state, sending 'busy-flash'.\n", ss5_state_names[ss5->state]);
|
PDEBUG_CHAN(DSS5, DEBUG_INFO, "Incoming call has disconnected in '%s' state, sending 'busy-flash'.\n", state_name);
|
||||||
/* send busy-flash */
|
/* send busy-flash */
|
||||||
set_tone(&ss5->dsp, 'B', MAX_BUSY_FLASH);
|
set_tone(&ss5->dsp, 'B', MAX_BUSY_FLASH);
|
||||||
/* change state */
|
/* change state */
|
||||||
ss5_new_state(ss5, SS5_STATE_SEND_BUSY);
|
ss5_new_state(ss5, SS5_STATE_SEND_BUSY);
|
||||||
} else
|
} else
|
||||||
if (ss5->state == SS5_STATE_IN_ACTIVE) {
|
if (ss5->state == SS5_STATE_IN_ACTIVE) {
|
||||||
PDEBUG_CHAN(DSS5, DEBUG_INFO, "Incoming call has disconnected in '%s' state, sending 'clear-back'.\n", ss5_state_names[ss5->state]);
|
PDEBUG_CHAN(DSS5, DEBUG_INFO, "Incoming call has disconnected in '%s' state, sending 'clear-back'.\n", state_name);
|
||||||
/* send clear-back */
|
/* send clear-back */
|
||||||
set_tone(&ss5->dsp, 'B', MAX_CLEAR_BACK);
|
set_tone(&ss5->dsp, 'B', MAX_CLEAR_BACK);
|
||||||
/* change state */
|
/* change state */
|
||||||
ss5_new_state(ss5, SS5_STATE_SEND_CLR_BAK);
|
ss5_new_state(ss5, SS5_STATE_SEND_CLR_BAK);
|
||||||
} else {
|
} else {
|
||||||
PDEBUG_CHAN(DSS5, DEBUG_INFO, "Outgoing call has disconnected in '%s' state, sending 'clear-forward'.\n", ss5_state_names[ss5->state]);
|
PDEBUG_CHAN(DSS5, DEBUG_INFO, "Outgoing call has disconnected in '%s' state, sending 'clear-forward'.\n", state_name);
|
||||||
/* send clear-forward */
|
/* send clear-forward */
|
||||||
set_tone(&ss5->dsp, 'C', MAX_CLEAR_FORWARD);
|
set_tone(&ss5->dsp, 'C', MAX_CLEAR_FORWARD);
|
||||||
/* change state */
|
/* change state */
|
||||||
|
|
Loading…
Reference in New Issue