Add option to cross-connect calls; Calls between mobiles are now possible

Use -x to enable call cross-connect. No MNCC socket, no call device must
be specified!

Be sure to have at least one control channel and two voice channels.
Alternatively you can use one combined control/voice channel and one
voice channel.
This commit is contained in:
Andreas Eversberg 2017-10-29 07:58:20 +01:00
parent 87a21a285a
commit e8a3306eee
5 changed files with 330 additions and 30 deletions

View File

@ -139,29 +139,6 @@ Finally store the settings using "alsactl store" command.
Do this whenever you want to keep your adjustments.
</p>
<p class="toppic">
Channel crossing
</p>
<p>
By default, the right channel of your sound adapter is used to connect to the radio.
Some adapters have mono input, especially on microphone jack.
In this case you will always receive audio, no matter if you cross the channels or not.
If you cross the channels, the left channel of your sound adapter is used.
I use mono cables with only one channel on the tip of the plug.
Usual sound adapters connect left channel on the tip of the plug, so we need to swap channels to use mono plugs.
Use the command line option '-x' or '--cross':
</p>
<pre>
# src/bnetz/bnetz -k 1 -x -l 2
bnetz.c:268 info : Entering IDLE state, sending 'Gruppenfreisignal' 2 on channel 1.
Base station ready, please tune transmitter to 153.010 MHz and receiver to 148.410 MHz.
To call phone, switch transmitter (using pilot signal) to 153.370 MHz.
</pre>
<p class="toppic">
Emphasis
</p>
@ -334,6 +311,44 @@ Current transceiver and call state can be viewed by pressing 'c' key.
Press 'c' again to turn off this view.
</p>
<p class="toppic">
Mobile to mobile calls
</p>
<p>
Calls can be forwarded between mobiles.
By default, only one call can be made with the built-in console.
If a headset is used, only one call can be made between headset and one mobile station.
If call forwarding is used, two (or more) mobile stations can directly call each other.
It is essential to have at least two voice channels of course.
Depending on the network, a control channel or alternatively a combined control+voice channel is required.
This feature makes sense for SDR only, because SDR can provide multiple voice and control channels.
(It is also possible to use two radio receivers and transmitters connected to a sound card.)
</p>
<p>
To forward calls, be sure to configure the network with at least two channels that support voice.
Add '-x' to your command line.
On one phone, enter the number of the other phone and start the call.
</p>
<pre>
nmt -k 1 -k 4 -a hw:0,0 -a hw:0,0 -T CC/TC -T TC -0 1 -0 2 -Y se,1 -x
</pre>
<p>
This example will run a base station with two channel (1 and 4) via two radios connectd to a stereo sound card.
The sound card is accessed via '-a hw:0,0'.
Because the sound card is stereo, the '-a' option can be given for two channels.
The first channel is a combined control+traffic channel and the second a traffic channel.
Both channels have different supervisory signals '-0 1 -0 2'.
The station code is '-Y se,1'.
Refer to NMT section about configuring an NMT network.
I highly recommend to use an SDR instead of radios connected to a sound card.
</p>
<hr><center>[<a href="index.html">Back to main page</a>]</center><hr>
</td></tr></table></center>
</body>

View File

@ -28,6 +28,7 @@ libmobile_a_SOURCES = \
call.c \
testton.c \
mncc_console.c \
mncc_cross.c \
mncc_sock.c \
hagelbarger.c \
display_status.c \

View File

@ -37,6 +37,7 @@
#include "call.h"
#include "mncc_console.h"
#include "mncc_sock.h"
#include "mncc_cross.h"
#ifdef HAVE_SDR
#include "sdr.h"
#include "sdr_config.h"
@ -61,6 +62,7 @@ int do_de_emphasis = 0;
double rx_gain = 1.0;
static int echo_test = 0;
static int use_mncc_sock = 0;
static int use_mncc_cross = 0;
const char *mncc_name = "";
static int send_patterns = 1;
static int release_on_disconnect = 1;
@ -124,6 +126,10 @@ void main_mobile_print_help(const char *arg0, const char *ext_usage)
printf(" Sound card and device number for headset (default = '%s')\n", call_audiodev);
printf(" --call-samplerate <rate>\n");
printf(" Sample rate of sound device for headset (default = '%d')\n", call_samplerate);
printf(" -x --mncc-cross\n");
printf(" Enable built-in call forwarding between mobiles. Be sure to have\n");
printf(" at least one control channel and two voice channels. Alternatively\n");
printf(" use one combined control+voice channel and one voice channels.\n");
printf(" -m --mncc-sock\n");
printf(" Disable built-in call contol and offer socket (to LCR)\n");
printf(" --mncc-name <name>\n");
@ -184,6 +190,7 @@ static struct option main_mobile_long_options[] = {
{"de-emphasis", 0, 0, 'd'},
{"rx-gain", 1, 0, 'g'},
{"echo-test", 0, 0, 'e'},
{"mncc-cross", 0, 0, 'x'},
{"mncc-sock", 0, 0, 'm'},
{"mncc-name", 1, 0, OPT_MNCC_NAME},
{"call-device", 1, 0, 'c'},
@ -198,7 +205,7 @@ static struct option main_mobile_long_options[] = {
{0, 0, 0, 0}
};
static const char *main_mobile_optstring = "hv:k:a:s:i:b:pdg:mec:t:l:r:";
static const char *main_mobile_optstring = "hv:k:a:s:i:b:pdg:exmc:t:l:r:";
struct option *long_options;
char *optstring;
@ -320,6 +327,10 @@ void main_mobile_opt_switch(int c, char *arg0, int *skip_args)
echo_test = 1;
*skip_args += 1;
break;
case 'x':
use_mncc_cross = 1;
*skip_args += 1;
break;
case 'm':
use_mncc_sock = 1;
*skip_args += 1;
@ -427,16 +438,32 @@ void main_mobile(int *quit, int latency, int interval, void (*myhandler)(void),
latspl = samplerate * latency / 1000;
/* check MNCC support */
if (use_mncc_sock && call_audiodev[0]) {
fprintf(stderr, "You selected MNCC interface, but it cannot be used with call device (headset).\n");
if (use_mncc_cross && num_kanal == 1) {
fprintf(stderr, "You selected built-in call forwarding, but only channel is used. Does this makes sense?\n");
return;
}
if (use_mncc_sock && echo_test) {
fprintf(stderr, "You selected MNCC interface, but it cannot be used with echo test.\n");
if (use_mncc_sock && use_mncc_cross) {
fprintf(stderr, "You selected MNCC socket interface and built-in call forwarding, but only one can be selected.\n");
return;
}
if (echo_test && call_audiodev[0]) {
fprintf(stderr, "You selected call device (headset), but it cannot be used with echo test.\n");
fprintf(stderr, "You selected call device (headset) and echo test, but only one can be selected.\n");
return;
}
if (use_mncc_sock && call_audiodev[0]) {
fprintf(stderr, "You selected MNCC socket interface, but it cannot be used with call device (headset).\n");
return;
}
if (use_mncc_cross && call_audiodev[0]) {
fprintf(stderr, "You selected built-in call forwarding, but it cannot be used with call device (headset).\n");
return;
}
if (use_mncc_sock && echo_test) {
fprintf(stderr, "You selected MNCC socket interface, but it cannot be used with echo test.\n");
return;
}
if (use_mncc_cross && echo_test) {
fprintf(stderr, "You selected built-in call forwarding, but it cannot be used with echo test.\n");
return;
}
@ -453,6 +480,12 @@ void main_mobile(int *quit, int latency, int interval, void (*myhandler)(void),
fprintf(stderr, "Failed to setup MNCC socket. Quitting!\n");
return;
}
} else if (use_mncc_cross) {
rc = mncc_cross_init();
if (rc < 0) {
fprintf(stderr, "Failed to setup MNCC crossing process. Quitting!\n");
return;
}
} else {
console_init(station_id, call_audiodev, call_samplerate, latency, station_id_digits, loopback, echo_test);
}
@ -598,6 +631,8 @@ next_char:
/* process call control */
if (use_mncc_sock)
mncc_sock_handle();
else if (use_mncc_cross)
mncc_cross_handle();
else
process_console(c);
@ -639,11 +674,15 @@ next_char:
}
/* cleanup call control */
if (!use_mncc_sock)
if (!use_mncc_sock && !use_mncc_cross)
console_cleanup();
/* close mncc socket */
if (use_mncc_sock)
mncc_sock_exit();
/* close mncc forwarding */
if (use_mncc_cross)
mncc_cross_exit();
}

240
src/common/mncc_cross.c Normal file
View File

@ -0,0 +1,240 @@
/* Mobie Network Call Control (MNCC) cross connecting mobiles
*
* (C) 2017 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 <stdint.h>
#include <errno.h>
#include <string.h>
#include <stdlib.h>
#include "sample.h"
#include "debug.h"
#include "call.h"
#include "cause.h"
#include "mncc.h"
#include "mncc_cross.h"
int new_callref = 0; /* toward mobile */
typedef struct cross {
struct cross *next;
int callref1;
int callref2;
} cross_t;
static cross_t *cross_head = NULL;
static int create_cross(int callref)
{
cross_t *cross;
cross = calloc(1, sizeof(*cross));
if (!cross) {
PDEBUG(DMNCC, DEBUG_ERROR, "No memory!\n");
abort();
}
cross->callref1 = callref;
cross->callref2 = ++new_callref;
/* attach to list */
cross->next = cross_head;
cross_head = cross;
PDEBUG(DMNCC, DEBUG_INFO, "Cross connection created\n");
return cross->callref2;
}
static void destroy_cross(cross_t *cross)
{
cross_t **crossp;
/* detach from list */
crossp = &cross_head;
while (*crossp && *crossp != cross)
crossp = &((*crossp)->next);
if (!(*crossp)) {
PDEBUG(DMNCC, DEBUG_ERROR, "Transaction not in list, please fix!!\n");
abort();
}
*crossp = cross->next;
free(cross);
PDEBUG(DMNCC, DEBUG_INFO, "Cross connection destroyed\n");
}
typedef struct queue {
struct queue *next;
int length;
uint8_t buf[0];
} queue_t;
static queue_t *queue_head;
static void cross_mncc_up(uint8_t *buf, int length);
static int cross_mncc_up_queue(uint8_t *buf, int length)
{
struct gsm_mncc *mncc = (struct gsm_mncc *)buf;
queue_t *queue, **queuep;
/* directly forward voice */
if (mncc->msg_type == ANALOG_8000HZ) {
cross_mncc_up(buf, length);
return 0;
}
/* queue all other messages */
queue = calloc(1, sizeof(*queue) + length);
if (!queue) {
PDEBUG(DMNCC, DEBUG_ERROR, "No memory!\n");
return -CAUSE_TEMPFAIL;
}
queue->length = length;
memcpy(queue->buf, buf, length);
/* add tail */
queuep = &queue_head;
while (*queuep)
queuep = &((*queuep)->next);
*queuep = queue;
return 0;
}
static void cross_mncc_up(uint8_t *buf, int length)
{
struct gsm_mncc *mncc = (struct gsm_mncc *)buf;
cross_t *cross = NULL;
int callref = mncc->callref, remote = 0;
/* find cross instance */
for (cross = cross_head; cross; cross = cross->next) {
if (cross->callref1 == callref) {
remote = cross->callref2;
break;
}
if (cross->callref2 == callref) {
remote = cross->callref1;
break;
}
}
if (mncc->msg_type == MNCC_REL_CNF) {
if (cross)
destroy_cross(cross);
return;
}
if (!remote && mncc->msg_type != MNCC_SETUP_IND) {
PDEBUG(DMNCC, DEBUG_ERROR, "invalid call ref.\n");
/* send down reused MNCC */
mncc->msg_type = MNCC_REL_REQ;
mncc->fields |= MNCC_F_CAUSE;
mncc->cause.location = LOCATION_USER;
mncc->cause.value = CAUSE_INVALCALLREF;
mncc_down(buf, length);
return;
}
switch (mncc->msg_type) {
case ANALOG_8000HZ:
/* send down reused MNCC */
mncc->callref = remote;
mncc_down(buf, length);
break;
case MNCC_SETUP_IND:
remote = create_cross(callref);
/* send down reused MNCC */
mncc->msg_type = MNCC_SETUP_REQ;
mncc->callref = remote;
mncc_down(buf, length);
break;
case MNCC_CALL_CONF_IND:
/* send down reused MNCC */
mncc->msg_type = MNCC_CALL_PROC_REQ;
mncc->callref = remote;
mncc_down(buf, length);
break;
case MNCC_ALERT_IND:
/* send down reused MNCC */
mncc->msg_type = MNCC_ALERT_REQ;
mncc->callref = remote;
mncc_down(buf, length);
break;
case MNCC_SETUP_CNF:
/* send down reused MNCC */
mncc->msg_type = MNCC_SETUP_RSP;
mncc->callref = remote;
mncc_down(buf, length);
break;
case MNCC_SETUP_COMPL_IND:
/* send down reused MNCC */
mncc->msg_type = MNCC_SETUP_COMPL_REQ;
mncc->callref = remote;
mncc_down(buf, length);
break;
case MNCC_DISC_IND:
/* send down reused MNCC */
mncc->msg_type = MNCC_DISC_REQ;
mncc->callref = remote;
mncc_down(buf, length);
break;
case MNCC_REL_IND:
/* send down reused MNCC */
mncc->msg_type = MNCC_REL_REQ;
mncc->callref = remote;
mncc_down(buf, length);
destroy_cross(cross);
break;
}
}
void mncc_cross_handle(void)
{
queue_t *queue;
while (queue_head) {
/* remove from head */
queue = queue_head;
queue_head = queue->next;
cross_mncc_up(queue->buf, queue->length);
free(queue);
}
}
int mncc_cross_init(void)
{
mncc_up = cross_mncc_up_queue;
PDEBUG(DMNCC, DEBUG_DEBUG, "MNCC crossconnect initialized, waiting for connection.\n");
return 0;
}
void mncc_cross_exit(void)
{
while (cross_head)
destroy_cross(cross_head);
PDEBUG(DMNCC, DEBUG_DEBUG, "MNCC crossconnect removed.\n");
}

5
src/common/mncc_cross.h Normal file
View File

@ -0,0 +1,5 @@
void mncc_cross_handle(void);
int mncc_cross_init(void);
void mncc_cross_exit(void);