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:
parent
87a21a285a
commit
e8a3306eee
|
@ -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>
|
||||
|
|
|
@ -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 \
|
||||
|
|
|
@ -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();
|
||||
}
|
||||
|
||||
|
|
|
@ -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");
|
||||
}
|
||||
|
|
@ -0,0 +1,5 @@
|
|||
|
||||
void mncc_cross_handle(void);
|
||||
int mncc_cross_init(void);
|
||||
void mncc_cross_exit(void);
|
||||
|
Loading…
Reference in New Issue