aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--src/anetz/anetz.c4
-rw-r--r--src/anetz/anetz.h2
-rw-r--r--src/anetz/main.c23
-rw-r--r--src/bnetz/bnetz.c4
-rw-r--r--src/bnetz/bnetz.h2
-rw-r--r--src/bnetz/main.c40
-rw-r--r--src/cnetz/cnetz.c282
-rw-r--r--src/cnetz/cnetz.h18
-rw-r--r--src/cnetz/dsp.c58
-rw-r--r--src/cnetz/main.c73
-rw-r--r--src/common/call.c2
-rw-r--r--src/common/main.h15
-rw-r--r--src/common/main_common.c27
-rw-r--r--src/common/sender.c166
-rw-r--r--src/common/sender.h8
-rw-r--r--src/common/sound.h5
-rw-r--r--src/common/sound_alsa.c31
-rw-r--r--src/nmt/main.c47
-rw-r--r--src/nmt/nmt.c57
-rw-r--r--src/nmt/nmt.h6
20 files changed, 709 insertions, 161 deletions
diff --git a/src/anetz/anetz.c b/src/anetz/anetz.c
index 8c1384a..c4993dd 100644
--- a/src/anetz/anetz.c
+++ b/src/anetz/anetz.c
@@ -137,7 +137,7 @@ static void anetz_timeout(struct timer *timer);
static void anetz_go_idle(anetz_t *anetz);
/* Create transceiver instance and link to a list. */
-int anetz_create(const char *sounddev, int samplerate, int pre_emphasis, int de_emphasis, const char *write_wave, const char *read_wave, int kanal, int loopback, double loss_volume)
+int anetz_create(int kanal, const char *sounddev, int samplerate, int cross_channels, int pre_emphasis, int de_emphasis, const char *write_wave, const char *read_wave, int loopback, double loss_volume)
{
anetz_t *anetz;
int rc;
@@ -156,7 +156,7 @@ int anetz_create(const char *sounddev, int samplerate, int pre_emphasis, int de_
PDEBUG(DANETZ, DEBUG_DEBUG, "Creating 'A-Netz' instance for 'Kanal' = %d (sample rate %d).\n", kanal, samplerate);
/* init general part of transceiver */
- rc = sender_create(&anetz->sender, sounddev, samplerate, pre_emphasis, de_emphasis, write_wave, read_wave, kanal, loopback, loss_volume, -1);
+ rc = sender_create(&anetz->sender, kanal, sounddev, samplerate, cross_channels, pre_emphasis, de_emphasis, write_wave, read_wave, loopback, loss_volume, -1);
if (rc < 0) {
PDEBUG(DANETZ, DEBUG_ERROR, "Failed to init 'Sender' processing!\n");
goto error;
diff --git a/src/anetz/anetz.h b/src/anetz/anetz.h
index 9063bed..2cc1643 100644
--- a/src/anetz/anetz.h
+++ b/src/anetz/anetz.h
@@ -41,7 +41,7 @@ typedef struct anetz {
double anetz_kanal2freq(int kanal, int unterband);
int anetz_init(void);
-int anetz_create(const char *sounddev, int samplerate, int pre_emphasis, int de_emphasis, const char *write_wave, const char *read_wave, int kanal, int loopback, double loss_volume);
+int anetz_create(int kanal, const char *sounddev, int samplerate, int cross_channels, int pre_emphasis, int de_emphasis, const char *write_wave, const char *read_wave, int loopback, double loss_volume);
void anetz_destroy(sender_t *sender);
void anetz_loss_indication(anetz_t *anetz);
void anetz_receive_tone(anetz_t *anetz, int bit);
diff --git a/src/anetz/main.c b/src/anetz/main.c
index 3fb5499..15596ba 100644
--- a/src/anetz/main.c
+++ b/src/anetz/main.c
@@ -97,6 +97,7 @@ int main(int argc, char *argv[])
int rc;
int skip_args;
const char *station_id = "";
+ int i;
/* init common tones */
init_freiton();
@@ -116,11 +117,17 @@ int main(int argc, char *argv[])
station_id += strlen(station_id) - 5;
}
- if (!kanal) {
+ if (!num_kanal) {
printf("No channel (\"Kanal\") is specified, I suggest channel 30.\n\n");
print_help(argv[-skip_args]);
return 0;
}
+ if (num_kanal == 1 && num_sounddev == 0)
+ num_sounddev = 1; /* use defualt */
+ if (num_kanal != num_sounddev) {
+ fprintf(stdout, "You need to specify as many sound devices as you have channels.\n");
+ exit(0);
+ }
if (!loopback)
print_image();
@@ -142,14 +149,14 @@ int main(int argc, char *argv[])
}
/* create transceiver instance */
- rc = anetz_create(sounddev, samplerate, do_pre_emphasis, do_de_emphasis, write_wave, read_wave, kanal, loopback, lossdetect / 100.0);
- if (rc < 0) {
- fprintf(stderr, "Failed to create \"Sender\" instance. Quitting!\n");
- goto fail;
+ for (i = 0; i < num_kanal; i++) {
+ rc = anetz_create(kanal[i], sounddev[i], samplerate, cross_channels, do_pre_emphasis, do_de_emphasis, write_wave, read_wave, loopback, lossdetect / 100.0);
+ if (rc < 0) {
+ fprintf(stderr, "Failed to create \"Sender\" instance. Quitting!\n");
+ goto fail;
+ }
+ printf("Base station on channel %d ready, please tune transmitter to %.3f MHz and receiver to %.3f MHz.\n", kanal[i], anetz_kanal2freq(kanal[i], 0), anetz_kanal2freq(kanal[i], 1));
}
- printf("Base station ready, please tune transmitter to %.3f MHz and receiver "
- "to %.3f MHz.\n", anetz_kanal2freq(kanal, 0),
- anetz_kanal2freq(kanal, 1));
signal(SIGINT,sighandler);
signal(SIGHUP,sighandler);
diff --git a/src/bnetz/bnetz.c b/src/bnetz/bnetz.c
index e7a941c..d1e94d3 100644
--- a/src/bnetz/bnetz.c
+++ b/src/bnetz/bnetz.c
@@ -242,7 +242,7 @@ static void bnetz_timeout(struct timer *timer);
static void bnetz_go_idle(bnetz_t *bnetz);
/* Create transceiver instance and link to a list. */
-int bnetz_create(const char *sounddev, int samplerate, int pre_emphasis, int de_emphasis, const char *write_wave, const char *read_wave, int kanal, int gfs, int loopback, double loss_factor, const char *pilot)
+int bnetz_create(int kanal, const char *sounddev, int samplerate, int cross_channels, int gfs, int pre_emphasis, int de_emphasis, const char *write_wave, const char *read_wave, int loopback, double loss_factor, const char *pilot)
{
bnetz_t *bnetz;
int use_pilot_tone = -1;
@@ -300,7 +300,7 @@ error_pilot:
PDEBUG(DBNETZ, DEBUG_DEBUG, "Creating 'B-Netz' instance for 'Kanal' = %d 'Gruppenfreisignal' = %d (sample rate %d).\n", kanal, gfs, samplerate);
/* init general part of transceiver */
- rc = sender_create(&bnetz->sender, sounddev, samplerate, pre_emphasis, de_emphasis, write_wave, read_wave, kanal, loopback, loss_factor, use_pilot_tone);
+ rc = sender_create(&bnetz->sender, kanal, sounddev, samplerate, cross_channels, pre_emphasis, de_emphasis, write_wave, read_wave, loopback, loss_factor, use_pilot_tone);
if (rc < 0) {
PDEBUG(DBNETZ, DEBUG_ERROR, "Failed to init transceiver process!\n");
goto error;
diff --git a/src/bnetz/bnetz.h b/src/bnetz/bnetz.h
index c61cade..e83b371 100644
--- a/src/bnetz/bnetz.h
+++ b/src/bnetz/bnetz.h
@@ -90,7 +90,7 @@ typedef struct bnetz {
double bnetz_kanal2freq(int kanal, int unterband);
int bnetz_init(void);
-int bnetz_create(const char *sounddev, int samplerate, int pre_emphasis, int de_emphasis, const char *write_wave, const char *read_wave, int kanal, int gfs, int loopback, double loss_volume, const char *pilot);
+int bnetz_create(int kanal, const char *sounddev, int samplerate, int cross_channels, int gfs, int pre_emphasis, int de_emphasis, const char *write_wave, const char *read_wave, int loopback, double loss_factor, const char *pilot);
void bnetz_destroy(sender_t *sender);
void bnetz_loss_indication(bnetz_t *bnetz);
void bnetz_receive_tone(bnetz_t *bnetz, int bit);
diff --git a/src/bnetz/main.c b/src/bnetz/main.c
index 91e71de..97c32c8 100644
--- a/src/bnetz/main.c
+++ b/src/bnetz/main.c
@@ -37,7 +37,8 @@
#include "ansage.h"
int gfs = 2;
-const char *pilot = "tone";
+int num_pilot = 0;
+const char *pilot[MAX_SENDER] = { "tone" };
double lossdetect = 0;
void print_help(const char *arg0)
@@ -49,13 +50,13 @@ void print_help(const char *arg0)
printf(" -P --pilot tone | positive | negative | <file>=<on>:<off>\n");
printf(" Send a tone, give a signal or write to a file when switching to\n");
printf(" channel 19. (paging the phone).\n");
- printf(" 'tone', 'positive', 'negative' is sent on right audio channel.\n");
+ printf(" 'tone', 'positive', 'negative' is sent on second audio channel.\n");
printf(" 'tone' sends a tone whenever channel 19 is switchted.\n");
printf(" 'positive' sends a positive signal for channel 19, else negative.\n");
printf(" 'negative' sends a negative signal for channel 19, else positive.\n");
printf(" Example: /sys/class/gpio/gpio17/value=1:0 writes a '1' to\n");
printf(" /sys/class/gpio/gpio17/value to switching to channel 19 and a '0' to\n");
- printf(" switch back. (default = %s)\n", pilot);
+ printf(" switch back. (default = %s)\n", pilot[0]);
printf(" -0 --loss <volume>\n");
printf(" Detect loss of carrier by detecting steady noise above given volume in\n");
printf(" percent. (disabled by default)\n");
@@ -90,7 +91,7 @@ static int handle_options(int argc, char **argv)
skip_args += 2;
break;
case 'P':
- pilot = strdup(optarg);
+ OPT_ARRAY(num_pilot, pilot, strdup(optarg))
skip_args += 2;
break;
case '0':
@@ -110,6 +111,7 @@ int main(int argc, char *argv[])
int rc;
int skip_args;
const char *station_id = "";
+ int i;
/* init common tones */
init_freiton();
@@ -128,11 +130,23 @@ int main(int argc, char *argv[])
}
}
- if (!kanal) {
+ if (!num_kanal) {
printf("No channel (\"Kanal\") is specified, I suggest channel 1.\n\n");
print_help(argv[-skip_args]);
return 0;
}
+ if (num_kanal == 1 && num_sounddev == 0)
+ num_sounddev = 1; /* use defualt */
+ if (num_kanal != num_sounddev) {
+ fprintf(stdout, "You need to specify as many sound devices as you have channels.\n");
+ exit(0);
+ }
+ if (num_kanal == 1 && num_pilot == 0)
+ num_pilot = 1; /* use defualt */
+ if (num_kanal != num_pilot) {
+ fprintf(stdout, "You need to specify as many pilot tone settings as you have channels.\n");
+ exit(0);
+ }
if (!loopback)
print_image();
@@ -154,15 +168,15 @@ int main(int argc, char *argv[])
}
/* create transceiver instance */
- rc = bnetz_create(sounddev, samplerate, do_pre_emphasis, do_de_emphasis, write_wave, read_wave, kanal, gfs, loopback, (double)lossdetect / 100.0, pilot);
- if (rc < 0) {
- fprintf(stderr, "Failed to create \"Sender\" instance. Quitting!\n");
- goto fail;
+ for (i = 0; i < num_kanal; i++) {
+ rc = bnetz_create(kanal[i], sounddev[i], samplerate, cross_channels, gfs, do_pre_emphasis, do_de_emphasis, write_wave, read_wave, loopback, (double)lossdetect / 100.0, pilot[i]);
+ if (rc < 0) {
+ fprintf(stderr, "Failed to create \"Sender\" instance. Quitting!\n");
+ goto fail;
+ }
+ printf("Base station for channel %d ready, please tune transmitter to %.3f MHz and receiver " "to %.3f MHz.\n", kanal[i], bnetz_kanal2freq(kanal[i], 0), bnetz_kanal2freq(kanal[i], 1));
+ printf("To call phone, switch transmitter (using pilot signal) to %.3f MHz.\n", bnetz_kanal2freq(19, 0));
}
- printf("Base station ready, please tune transmitter to %.3f MHz and receiver "
- "to %.3f MHz.\n", bnetz_kanal2freq(kanal, 0),
- bnetz_kanal2freq(kanal, 1));
- printf("To call phone, switch transmitter (using pilot signal) to %.3f MHz.\n", bnetz_kanal2freq(19, 0));
signal(SIGINT,sighandler);
signal(SIGHUP,sighandler);
diff --git a/src/cnetz/cnetz.c b/src/cnetz/cnetz.c
index bc74002..a2dca38 100644
--- a/src/cnetz/cnetz.c
+++ b/src/cnetz/cnetz.c
@@ -55,6 +55,31 @@ double cnetz_kanal2freq(int kanal, int unterband)
return freq;
}
+const char *cnetz_state_name(enum cnetz_state state)
+{
+ static char invalid[16];
+
+ switch (state) {
+ case CNETZ_NULL:
+ return "(NULL)";
+ case CNETZ_IDLE:
+ return "IDLE";
+ case CNETZ_BUSY:
+ return "BUSY";
+ }
+
+ sprintf(invalid, "invalid(%d)", state);
+ return invalid;
+}
+
+static void cnetz_new_state(cnetz_t *cnetz, enum cnetz_state new_state)
+{
+ if (cnetz->state == new_state)
+ return;
+ PDEBUG(DCNETZ, DEBUG_DEBUG, "State change: %s -> %s\n", cnetz_state_name(cnetz->state), cnetz_state_name(new_state));
+ cnetz->state = new_state;
+}
+
/* Convert ISDN cause to 'Ausloesegrund' of C-Netz mobile station */
uint8_t cnetz_cause_isdn2cnetz(int cause)
{
@@ -86,8 +111,9 @@ static void trans_new_state(transaction_t *trans, int state);
static void cnetz_flush_other_transactions(cnetz_t *cnetz, transaction_t *trans);
/* Create transceiver instance and link to a list. */
-int cnetz_create(const char *sounddev, int samplerate, int pre_emphasis, int de_emphasis, const char *write_wave, const char *read_wave, int kanal, int auth, int ms_power, int measure_speed, double clock_speed[2], double deviation, double noise, int loopback)
+int cnetz_create(int kanal, enum cnetz_chan_type chan_type, const char *sounddev, int samplerate, int cross_channels, int auth, int ms_power, int measure_speed, double clock_speed[2], double deviation, double noise, int pre_emphasis, int de_emphasis, const char *write_wave, const char *read_wave, int loopback)
{
+ sender_t *sender;
cnetz_t *cnetz;
int rc;
@@ -103,8 +129,29 @@ int cnetz_create(const char *sounddev, int samplerate, int pre_emphasis, int de_
PDEBUG(DCNETZ, DEBUG_NOTICE, "Channel ('Kanal') number %d is specified as 'unused', it might not work!\n", kanal);
}
- if (kanal == CNETZ_OGK_KANAL) {
- PDEBUG(DCNETZ, DEBUG_NOTICE, "You selected channel %d ('Orga-Kanal') for speech channel. Some phones will reject this.\n", CNETZ_OGK_KANAL);
+ /* OgK must be on channel 131 */
+ if ((chan_type == CHAN_TYPE_OGK || chan_type == CHAN_TYPE_OGK_SPK) && kanal != CNETZ_OGK_KANAL) {
+ PDEBUG(DCNETZ, DEBUG_NOTICE, "You must use channel %d for calling channel ('Orga-Kanal') or for combined calling + traffic channel!\n", CNETZ_OGK_KANAL);
+ return -EINVAL;
+ }
+
+ /* SpK must be on channel other than 131 */
+ if (chan_type == CHAN_TYPE_SPK && kanal == CNETZ_OGK_KANAL) {
+ PDEBUG(DCNETZ, DEBUG_NOTICE, "You must not use channel %d for traffic channel!\n", CNETZ_OGK_KANAL);
+ return -EINVAL;
+ }
+
+ /* warn if we combine SpK and OgK, this is not supported by standard */
+ if (chan_type == CHAN_TYPE_OGK_SPK) {
+ PDEBUG(DCNETZ, DEBUG_NOTICE, "You selected channel %d ('Orga-Kanal') for combined calling + traffic channel. Some phones will reject this.\n", CNETZ_OGK_KANAL);
+ }
+
+ for (sender = sender_head; sender; sender = sender->next) {
+ cnetz = (cnetz_t *)sender;
+ if (!!strcmp(sender->sounddev, sounddev)) {
+ PDEBUG(DCNETZ, DEBUG_NOTICE, "To be able to sync multiple channels, all channels must be on the same sound device!\n");
+ return -EINVAL;
+ }
}
cnetz = calloc(1, sizeof(cnetz_t));
@@ -117,12 +164,20 @@ int cnetz_create(const char *sounddev, int samplerate, int pre_emphasis, int de_
/* init general part of transceiver */
/* do not enable emphasis, since it is done by cnetz code, not by common sender code */
- rc = sender_create(&cnetz->sender, sounddev, samplerate, 0, 0, write_wave, read_wave, kanal, loopback, 0, -1);
+ rc = sender_create(&cnetz->sender, kanal, sounddev, samplerate, cross_channels, 0, 0, write_wave, read_wave, loopback, 0, -1);
if (rc < 0) {
PDEBUG(DCNETZ, DEBUG_ERROR, "Failed to init transceiver process!\n");
goto error;
}
+#if 0
+ #warning hacking: applying different clock to slave
+ if (&cnetz->sender != sender_head) {
+ clock_speed[0] = -3;
+ clock_speed[1] = -3;
+ }
+#endif
+
/* init audio processing */
rc = dsp_init_sender(cnetz, measure_speed, clock_speed, deviation, noise);
if (rc < 0) {
@@ -130,6 +185,7 @@ int cnetz_create(const char *sounddev, int samplerate, int pre_emphasis, int de_
goto error;
}
+ cnetz->chan_type = chan_type;
cnetz->auth = auth;
cnetz->ms_power = ms_power;
@@ -150,6 +206,14 @@ int cnetz_create(const char *sounddev, int samplerate, int pre_emphasis, int de_
trans->mo_call = 1;
cnetz->sched_switch_mode = 2;
cnetz->sched_dsp_mode = DSP_MODE_SPK_K;
+#else
+ /* create transaction for speech channel loopback */
+ if (loopback && chan_type == CHAN_TYPE_SPK) {
+ transaction_t *trans = create_transaction(cnetz, TRANS_VHQ, 2, 2, 22002);
+ trans->mo_call = 1;
+ cnetz->dsp_mode = DSP_MODE_SPK_K;
+ cnetz->sched_dsp_mode = DSP_MODE_SPK_K;
+ }
#endif
return 0;
@@ -189,15 +253,18 @@ static void cnetz_go_idle(cnetz_t *cnetz)
}
/* set scheduler to OgK */
- PDEBUG(DBNETZ, DEBUG_INFO, "Entering IDLE state, sending 'Funkzellenkennung' %d,%d,%d.\n", si.fuz_nat, si.fuz_fuvst, si.fuz_rest);
- cnetz->state = CNETZ_IDLE;
+ if (cnetz->sender.kanal == CNETZ_OGK_KANAL)
+ PDEBUG(DBNETZ, DEBUG_INFO, "Entering IDLE state on channel %d, sending 'Funkzellenkennung' %d,%d,%d.\n", cnetz->sender.kanal, si.fuz_nat, si.fuz_fuvst, si.fuz_rest);
+ else
+ PDEBUG(DBNETZ, DEBUG_INFO, "Entering IDLE state on channel %d.\n", cnetz->sender.kanal, si.fuz_nat, si.fuz_fuvst, si.fuz_rest);
+ cnetz_new_state(cnetz, CNETZ_IDLE);
if (cnetz->dsp_mode == DSP_MODE_SPK_K || cnetz->dsp_mode == DSP_MODE_SPK_V) {
/* go idle after next frame/slot */
cnetz->sched_switch_mode = 1;
- cnetz->sched_dsp_mode = DSP_MODE_OGK;
+ cnetz->sched_dsp_mode = (cnetz->sender.kanal == CNETZ_OGK_KANAL) ? DSP_MODE_OGK : DSP_MODE_OFF;
} else {
cnetz->sched_switch_mode = 0;
- cnetz->dsp_mode = DSP_MODE_OGK;
+ cnetz->dsp_mode = (cnetz->sender.kanal == CNETZ_OGK_KANAL) ? DSP_MODE_OGK : DSP_MODE_OFF;
}
}
@@ -231,6 +298,44 @@ void call_rx_audio(int callref, int16_t *samples, int count)
}
}
+cnetz_t *search_free_spk(void)
+{
+ sender_t *sender;
+ cnetz_t *cnetz, *ogk_spk = NULL;
+
+ for (sender = sender_head; sender; sender = sender->next) {
+ cnetz = (cnetz_t *) sender;
+ if (cnetz->state != CNETZ_IDLE)
+ continue;
+ /* return first free SpK */
+ if (cnetz->chan_type == CHAN_TYPE_SPK)
+ return cnetz;
+ /* remember OgK/SpK combined channel as second alternative */
+ if (cnetz->chan_type == CHAN_TYPE_OGK_SPK)
+ ogk_spk = cnetz;
+ }
+
+ return ogk_spk;
+}
+
+cnetz_t *search_ogk(void)
+{
+ sender_t *sender;
+ cnetz_t *cnetz;
+
+ for (sender = sender_head; sender; sender = sender->next) {
+ cnetz = (cnetz_t *) sender;
+ if (cnetz->state != CNETZ_IDLE)
+ continue;
+ if (cnetz->chan_type == CHAN_TYPE_OGK)
+ return cnetz;
+ if (cnetz->chan_type == CHAN_TYPE_OGK_SPK)
+ return cnetz;
+ }
+
+ return NULL;
+}
+
int call_out_setup(int callref, char *dialing)
{
sender_t *sender;
@@ -267,12 +372,11 @@ inval:
cnetz = (cnetz_t *) sender;
/* search transaction for this number */
trans = cnetz->trans_list;
- while (trans) {
+ for (trans = cnetz->trans_list; trans; trans = trans->next) {
if (trans->futln_nat == futln_nat
&& trans->futln_fuvst == futln_fuvst
&& trans->futln_rest == futln_rest)
break;
- trans = trans->next;
}
if (trans)
break;
@@ -283,20 +387,21 @@ inval:
}
/* 3. check if all senders are busy, return NOCHANNEL */
- for (sender = sender_head; sender; sender = sender->next) {
- cnetz = (cnetz_t *) sender;
- if (cnetz->state == CNETZ_IDLE)
- break;
- }
- if (!sender) {
+ if (!search_free_spk()) {
PDEBUG(DCNETZ, DEBUG_NOTICE, "Outgoing call, but no free channel, rejecting!\n");
return -CAUSE_NOCHANNEL;
}
+ cnetz = search_ogk();
+ if (!cnetz) {
+ PDEBUG(DCNETZ, DEBUG_NOTICE, "Outgoing call, but OgK is currently busy, rejecting!\n");
+ return -CAUSE_NOCHANNEL;
+ }
+
PDEBUG(DCNETZ, DEBUG_INFO, "Call to mobile station, paging station id '%s'\n", dialing);
/* 4. trying to page mobile station */
- sender->callref = callref;
+ cnetz->sender.callref = callref;
trans = create_transaction(cnetz, TRANS_VAK, dialing[0] - '0', dialing[1] - '0', atoi(dialing + 2));
if (!trans) {
@@ -304,9 +409,6 @@ inval:
sender->callref = 0;
return -CAUSE_TEMPFAIL;
}
- cnetz->state = CNETZ_BUSY;
- /* flush all other transactions, if any */
- cnetz_flush_other_transactions(cnetz, trans);
return 0;
}
@@ -418,10 +520,23 @@ void call_out_release(int callref, int cause)
static void transaction_timeout(struct timer *timer);
+/* link transaction to list */
+static void link_transaction(transaction_t *trans, cnetz_t *cnetz)
+{
+ transaction_t **transp;
+
+ /* attach to end of list, so first transaction is served first */
+ trans->cnetz = cnetz;
+ transp = &cnetz->trans_list;
+ while (*transp)
+ transp = &((*transp)->next);
+ *transp = trans;
+}
+
/* create transaction */
static transaction_t *create_transaction(cnetz_t *cnetz, uint32_t state, uint8_t futln_nat, uint8_t futln_fuvst, uint16_t futln_rest)
{
- transaction_t *trans, **transp;
+ transaction_t *trans;
/* search transaction for this subsriber */
trans = cnetz->trans_list;
@@ -458,18 +573,13 @@ static transaction_t *create_transaction(cnetz_t *cnetz, uint32_t state, uint8_t
const char *rufnummer = transaction2rufnummer(trans);
PDEBUG(DCNETZ, DEBUG_INFO, "Created transaction for subscriber '%s'\n", rufnummer);
- /* attach to end of list, so first transaction is served first */
- trans->cnetz = cnetz;
- transp = &cnetz->trans_list;
- while (*transp)
- transp = &((*transp)->next);
- *transp = trans;
+ link_transaction(trans, cnetz);
return trans;
}
-/* destroy transaction */
-static void destroy_transaction(transaction_t *trans)
+/* unlink transaction from list */
+static void unlink_transaction(transaction_t *trans)
{
transaction_t **transp;
@@ -482,6 +592,13 @@ static void destroy_transaction(transaction_t *trans)
abort();
}
*transp = trans->next;
+ trans->cnetz = NULL;
+}
+
+/* destroy transaction */
+static void destroy_transaction(transaction_t *trans)
+{
+ unlink_transaction(trans);
const char *rufnummer = transaction2rufnummer(trans);
PDEBUG(DCNETZ, DEBUG_INFO, "Destroying transaction for subscriber '%s'\n", rufnummer);
@@ -557,6 +674,65 @@ static void trans_new_state(transaction_t *trans, int state)
trans->state = state;
}
+static struct cnetz_channels {
+ enum cnetz_chan_type chan_type;
+ const char *short_name;
+ const char *long_name;
+} cnetz_channels[] = {
+ { CHAN_TYPE_OGK_SPK, "OgK/SpK","combined calling & traffic channel" },
+ { CHAN_TYPE_OGK, "OgK", "calling channel" },
+ { CHAN_TYPE_SPK, "SpK", "traffic channel" },
+ { 0, NULL, NULL }
+};
+
+void cnetz_channel_list(void)
+{
+ int i;
+
+ printf("Type\tDescription\n");
+ printf("------------------------------------------------------------------------\n");
+ for (i = 0; cnetz_channels[i].long_name; i++)
+ printf("%s\t%s\n", cnetz_channels[i].short_name, cnetz_channels[i].long_name);
+}
+
+int cnetz_channel_by_short_name(const char *short_name)
+{
+ int i;
+
+ for (i = 0; cnetz_channels[i].short_name; i++) {
+ if (!strcasecmp(cnetz_channels[i].short_name, short_name)) {
+ PDEBUG(DCNETZ, DEBUG_INFO, "Selecting channel '%s' = %s\n", cnetz_channels[i].short_name, cnetz_channels[i].long_name);
+ return cnetz_channels[i].chan_type;
+ }
+ }
+
+ return -1;
+}
+
+const char *chan_type_short_name(enum cnetz_chan_type chan_type)
+{
+ int i;
+
+ for (i = 0; cnetz_channels[i].short_name; i++) {
+ if (cnetz_channels[i].chan_type == chan_type)
+ return cnetz_channels[i].short_name;
+ }
+
+ return "invalid";
+}
+
+const char *chan_type_long_name(enum cnetz_chan_type chan_type)
+{
+ int i;
+
+ for (i = 0; cnetz_channels[i].long_name; i++) {
+ if (cnetz_channels[i].chan_type == chan_type)
+ return cnetz_channels[i].long_name;
+ }
+
+ return "invalid";
+}
+
/* Timeout handling */
static void transaction_timeout(struct timer *timer)
{
@@ -675,6 +851,7 @@ const telegramm_t *cnetz_transmit_telegramm_rufblock(cnetz_t *cnetz)
{
static telegramm_t telegramm;
transaction_t *trans;
+ cnetz_t *spk;
memset(&telegramm, 0, sizeof(telegramm));
@@ -716,12 +893,18 @@ const telegramm_t *cnetz_transmit_telegramm_rufblock(cnetz_t *cnetz)
destroy_transaction(trans);
break;
case TRANS_WBN:
+wbn:
PDEBUG(DCNETZ, DEBUG_INFO, "Sending call reject 'Wahlbestaetigung negativ'.\n");
telegramm.opcode = OPCODE_WBN_R;
destroy_transaction(trans);
cnetz_go_idle(cnetz);
break;
case TRANS_WBP:
+ spk = search_free_spk();
+ if (!spk) {
+ PDEBUG(DCNETZ, DEBUG_NOTICE, "No free channel anymore, rejecting call!\n");
+ goto wbn;
+ }
PDEBUG(DCNETZ, DEBUG_INFO, "Sending call accept 'Wahlbestaetigung positiv'.\n");
telegramm.opcode = OPCODE_WBP_R;
trans_new_state(trans, TRANS_VAG);
@@ -735,13 +918,27 @@ const telegramm_t *cnetz_transmit_telegramm_rufblock(cnetz_t *cnetz)
PDEBUG(DCNETZ, DEBUG_INFO, "Sending channel assignment 'Verbindungsaufbau kommend'.\n");
telegramm.opcode = OPCODE_VAK_R;
}
- telegramm.frequenz_nr = cnetz->sender.kanal;
trans_new_state(trans, TRANS_BQ);
trans->count = 0;
timer_start(&trans->timer, 0.150 + 0.0375 * F_BQ); /* two slots + F_BQ frames */
+ /* select channel */
+ spk = search_free_spk();
+ if (spk == cnetz) {
+ PDEBUG(DCNETZ, DEBUG_INFO, "Staying on combined calling + traffic channel %d\n", spk->sender.kanal);
+ } else {
+ PDEBUG(DCNETZ, DEBUG_INFO, "Assigning phone to traffic channel %d\n", spk->sender.kanal);
+ /* sync RX time to current OgK time */
+ spk->fsk_demod.bit_time = cnetz->fsk_demod.bit_time;
+ }
+ telegramm.frequenz_nr = spk->sender.kanal;
+ cnetz_new_state(spk, CNETZ_BUSY);
/* schedule switching two slots ahead */
- cnetz->sched_switch_mode = 2;
- cnetz->sched_dsp_mode = DSP_MODE_SPK_K;
+ spk->sched_switch_mode = 2;
+ spk->sched_dsp_mode = DSP_MODE_SPK_K;
+ unlink_transaction(trans);
+ link_transaction(trans, spk);
+ /* flush all other transactions, if any (in case of OgK/SpK) */
+ cnetz_flush_other_transactions(spk, trans);
break;
default:
; /* LR */
@@ -792,6 +989,7 @@ void cnetz_receive_telegramm_ogk(cnetz_t *cnetz, telegramm_t *telegramm, int blo
int valid_frame = 0;
transaction_t *trans;
const char *rufnummer;
+ cnetz_t *spk;
switch (opcode) {
case OPCODE_EM_R:
@@ -803,7 +1001,7 @@ void cnetz_receive_telegramm_ogk(cnetz_t *cnetz, telegramm_t *telegramm, int blo
else
PDEBUG(DCNETZ, DEBUG_INFO, "Received Attachment 'Einbuchen' message from Subscriber '%s' with %s card's security code %d\n", rufnummer, (telegramm->chipkarten_futelg_bit) ? "chip":"magnet", telegramm->sicherungs_code);
if (cnetz->state != CNETZ_IDLE) {
- PDEBUG(DCNETZ, DEBUG_NOTICE, "Ignoring Attachment from subscriber '%s', because we are busy.\n", rufnummer);
+ PDEBUG(DCNETZ, DEBUG_NOTICE, "Ignoring Attachment from subscriber '%s', because we are busy becoming SpK.\n", rufnummer);
break;
}
trans = create_transaction(cnetz, TRANS_EM, telegramm->futln_nationalitaet, telegramm->futln_heimat_fuvst_nr, telegramm->futln_rest_nr);
@@ -822,7 +1020,7 @@ void cnetz_receive_telegramm_ogk(cnetz_t *cnetz, telegramm_t *telegramm, int blo
else
PDEBUG(DCNETZ, DEBUG_INFO, "Received Roaming 'Umbuchen' message from Subscriber '%s' with %s card's security code %d\n", rufnummer, (telegramm->chipkarten_futelg_bit) ? "chip":"magnet", telegramm->sicherungs_code);
if (cnetz->state != CNETZ_IDLE) {
- PDEBUG(DCNETZ, DEBUG_NOTICE, "Ignoring Roaming from subscriber '%s', because we are busy.\n", rufnummer);
+ PDEBUG(DCNETZ, DEBUG_NOTICE, "Ignoring Roaming from subscriber '%s', because we are busy becoming SpK.\n", rufnummer);
break;
}
trans = create_transaction(cnetz, TRANS_UM, telegramm->futln_nationalitaet, telegramm->futln_heimat_fuvst_nr, telegramm->futln_rest_nr);
@@ -839,7 +1037,7 @@ void cnetz_receive_telegramm_ogk(cnetz_t *cnetz, telegramm_t *telegramm, int blo
rufnummer = telegramm2rufnummer(telegramm);
PDEBUG(DCNETZ, DEBUG_INFO, "Received outgoing Call 'Verbindungswunsch gehend' message from Subscriber '%s'\n", rufnummer);
if (cnetz->state != CNETZ_IDLE) {
- PDEBUG(DCNETZ, DEBUG_NOTICE, "Ignoring Call from subscriber '%s', because we are busy.\n", rufnummer);
+ PDEBUG(DCNETZ, DEBUG_NOTICE, "Ignoring Call from subscriber '%s', because we are busy becoming SpK.\n", rufnummer);
break;
}
trans = create_transaction(cnetz, TRANS_VWG, telegramm->futln_nationalitaet, telegramm->futln_heimat_fuvst_nr, telegramm->futln_rest_nr);
@@ -847,9 +1045,12 @@ void cnetz_receive_telegramm_ogk(cnetz_t *cnetz, telegramm_t *telegramm, int blo
PDEBUG(DCNETZ, DEBUG_ERROR, "Failed to create transaction\n");
break;
}
- cnetz->state = CNETZ_BUSY;
- /* flush all other transactions, if any */
- cnetz_flush_other_transactions(cnetz, trans);
+ spk = search_free_spk();
+ if (!spk) {
+ PDEBUG(DCNETZ, DEBUG_NOTICE, "Rejecting call from subscriber '%s', because we have no free channel.\n", rufnummer);
+ trans_new_state(trans, TRANS_WBN);
+ break;
+ }
valid_frame = 1;
break;
case OPCODE_WUE_M:
@@ -915,9 +1116,10 @@ const telegramm_t *cnetz_transmit_telegramm_spk_k(cnetz_t *cnetz)
}
break;
case TRANS_VHQ:
- PDEBUG(DCNETZ, DEBUG_INFO, "Sending 'Quittung Verbindung halten' on traffic channel\n");
+ if (!cnetz->sender.loopback)
+ PDEBUG(DCNETZ, DEBUG_INFO, "Sending 'Quittung Verbindung halten' on traffic channel\n");
telegramm.opcode = OPCODE_VHQ_K;
- if ((cnetz->sched_ts & 7) == 7 && cnetz->sched_r_m && !timer_running(&trans->timer)) {
+ if (!cnetz->sender.loopback && (cnetz->sched_ts & 7) == 7 && cnetz->sched_r_m && !timer_running(&trans->timer)) {
/* next sub frame */
if (trans->mo_call) {
int callref = ++new_callref;
diff --git a/src/cnetz/cnetz.h b/src/cnetz/cnetz.h
index d0a979f..265fc04 100644
--- a/src/cnetz/cnetz.h
+++ b/src/cnetz/cnetz.h
@@ -8,13 +8,21 @@
/* dsp modes of transmission */
enum dsp_mode {
DSP_SCHED_NONE = 0, /* use for sheduling: nothing to shedule */
+ DSP_MODE_OFF, /* send nothing on unused SpK */
DSP_MODE_OGK, /* send "Telegramm" on OgK */
DSP_MODE_SPK_K, /* send concentrated "Telegramm" SpK */
DSP_MODE_SPK_V, /* send distributed "Telegramm" SpK */
};
+enum cnetz_chan_type {
+ CHAN_TYPE_OGK, /* pure standard organization channel (channel 131) */
+ CHAN_TYPE_SPK, /* pure traffic channel */
+ CHAN_TYPE_OGK_SPK, /* combined OGK + SPK; note: some phones may reject SPK on channel 131 */
+};
+
/* current state of c-netz sender */
enum cnetz_state {
+ CNETZ_NULL, /* before power on */
CNETZ_IDLE, /* broadcasting LR/MLR on Ogk */
CNETZ_BUSY, /* currently processing a call, no other transaction allowed */
};
@@ -85,6 +93,7 @@ struct clock_speed {
/* instance of cnetz sender */
typedef struct cnetz {
sender_t sender;
+ enum cnetz_chan_type chan_type; /* channel type */
scrambler_t scrambler_tx; /* mirror what we transmit to MS */
scrambler_t scrambler_rx; /* mirror what we receive from MS */
compander_t cstate;
@@ -125,6 +134,9 @@ typedef struct cnetz {
int dsp_speech_length; /* number of samples */
int dsp_speech_pos; /* current position in buffer */
+ int frame_last_count; /* master's count position of last frame sync */
+ double frame_last_phase; /* master's bit phase of last frame sync */
+
/* audio offset removal */
double offset_removal_factor; /* how much to remove every sample */
int16_t offset_last_sample; /* last sample of last audio chunk */
@@ -137,8 +149,12 @@ typedef struct cnetz {
} cnetz_t;
double cnetz_kanal2freq(int kanal, int unterband);
+void cnetz_channel_list(void);
+int cnetz_channel_by_short_name(const char *short_name);
+const char *chan_type_short_name(enum cnetz_chan_type chan_type);
+const char *chan_type_long_name(enum cnetz_chan_type chan_type);
int cnetz_init(void);
-int cnetz_create(const char *sounddev, int samplerate, int pre_emphasis, int de_emphasis, const char *write_wave, const char *read_wave, int kanal, int auth, int ms_power, int measure_speed, double clock_speed[2], double deviation, double noise, int loopback);
+int cnetz_create(int kanal, enum cnetz_chan_type chan_type, const char *sounddev, int samplerate, int cross_channels, int auth, int ms_power, int measure_speed, double clock_speed[2], double deviation, double noise, int pre_emphasis, int de_emphasis, const char *write_wave, const char *read_wave, int loopback);
void cnetz_destroy(sender_t *sender);
void cnetz_sync_frame(cnetz_t *cnetz, double sync, int ts);
const struct telegramm *cnetz_transmit_telegramm_rufblock(cnetz_t *cnetz);
diff --git a/src/cnetz/dsp.c b/src/cnetz/dsp.c
index f2e310d..1b5752f 100644
--- a/src/cnetz/dsp.c
+++ b/src/cnetz/dsp.c
@@ -542,7 +542,8 @@ void sender_receive(sender_t *sender, int16_t *samples, int length)
return;
#endif
- fsk_fm_demod(&cnetz->fsk_demod, samples, length);
+ if (cnetz->dsp_mode != DSP_MODE_OFF)
+ fsk_fm_demod(&cnetz->fsk_demod, samples, length);
return;
}
@@ -558,7 +559,7 @@ static int fsk_telegramm(cnetz_t *cnetz, int16_t *samples, int length)
again:
/* there must be length, otherwise we would skip blocks */
- if (!length)
+ if (count == length)
return count;
pos = cnetz->fsk_tx_buffer_pos;
@@ -567,8 +568,43 @@ again:
/* start new telegramm, so we generate one */
if (pos == 0) {
/* measure actual signal speed */
- if (cnetz->sched_ts == 0 && cnetz->sched_r_m == 0)
+ if (cnetz->sched_ts == 0 && cnetz->sched_r_m == 0) {
calc_clock_speed(cnetz, cnetz->sender.samplerate * 24 / 10, 1, 1);
+ /* sync TX */
+ if (cnetz->sender.slave) {
+ /* we are master, so we store sample count and phase */
+ cnetz->frame_last_count = count;
+ cnetz->frame_last_phase = cnetz->fsk_tx_phase;
+ }
+ if (cnetz->sender.master) {
+ /* we are slave, so we sync to sample count and phase */
+ cnetz_t *master = (cnetz_t *)cnetz->sender.master;
+ /* if we are still in sync with the sample count, we adjust the phase */
+ if (master->frame_last_count == count) {
+ PDEBUG(DDSP, DEBUG_DEBUG, "Sync slave to master: phase(master) = %.15f, phase(slave) = %.15f\n", master->frame_last_phase, cnetz->frame_last_phase);
+ cnetz->frame_last_phase = master->frame_last_phase;
+ }
+ /* only sync, if we do not hit the border of the sample chunk.
+ * this way we have slave and master sync at the same sample chunk. */
+ if (master->frame_last_count > 0 && master->frame_last_count < length - 1 && count > 0 && count < length - 1) {
+ /* if the sample count is one sample ahead, we go back one sample */
+ if (count == master->frame_last_count + 1) {
+ PDEBUG(DDSP, DEBUG_INFO, "Sync slave to master by going back one sample: phase(master) = %.15, phase(slave) = %.15\n", master->frame_last_phase, cnetz->frame_last_phase);
+ count--;
+ samples--;
+ cnetz->frame_last_phase = master->frame_last_phase;
+ }
+ /* if the sample count is one sample behind, we go forward one sample */
+ if (count == master->frame_last_count - 1) {
+ PDEBUG(DDSP, DEBUG_INFO, "Sync slave to master by going forth one sample: phase(master) = %.15, phase(slave) = %.15\n", master->frame_last_phase, cnetz->frame_last_phase);
+ count++;
+ *samples = samples[-1];
+ samples++;
+ cnetz->frame_last_phase = master->frame_last_phase;
+ }
+ }
+ }
+ }
/* switch to speech channel */
if (cnetz->sched_switch_mode && cnetz->sched_r_m == 0) {
@@ -606,6 +642,7 @@ again:
bits = cnetz_encode_telegramm(cnetz);
fsk_distributed_encode(cnetz, bits);
break;
+ case DSP_MODE_OFF:
default:
fsk_nothing_encode(cnetz);
}
@@ -627,8 +664,8 @@ again:
}
copy = cnetz->fsk_tx_buffer_length - pos;
- if (length < copy)
- copy = length;
+ if (length - count < copy)
+ copy = length - count;
for (i = 0; i < copy; i++) {
if (*spl == -32768) {
/* marker found to insert new chunk of audio */
@@ -654,7 +691,6 @@ again:
cnetz->dsp_speech_pos = speech_pos;
pos += copy;
count += copy;
- length -= copy;
if (pos == cnetz->fsk_tx_buffer_length) {
cnetz->fsk_tx_buffer_pos = 0;
goto again;
@@ -686,5 +722,15 @@ void sender_send(sender_t *sender, int16_t *samples, int length)
printf("this shall not happen, so please fix!\n");
exit(0);
}
+
+#if 0
+ #warning check phase between slave and master process
+ /* check for sync to master */
+ if (sender->master) {
+ if (((cnetz_t *)(sender->master))->fsk_tx_phase != cnetz->fsk_tx_phase) {
+ printf("phase between master and slave differs: %.10f vs %.10f!\n", ((cnetz_t *)(sender->master))->fsk_tx_phase, cnetz->fsk_tx_phase);
+ }
+ }
+#endif
}
diff --git a/src/cnetz/main.c b/src/cnetz/main.c
index 1a72440..d0c6ce2 100644
--- a/src/cnetz/main.c
+++ b/src/cnetz/main.c
@@ -39,6 +39,8 @@
#include "ansage.h"
/* settings */
+int num_chan_type = 0;
+enum cnetz_chan_type chan_type[MAX_SENDER] = { CHAN_TYPE_OGK_SPK };
int measure_speed = 0;
double clock_speed[2] = { 0.0, 0.0 };
int set_clock_speed = 0;
@@ -50,13 +52,16 @@ void print_help(const char *arg0)
{
print_help_common(arg0, "");
/* - - */
+ printf(" -t --channel-type <channel type> | list\n");
+ printf(" Give channel type, use 'list' to get a list. (default = '%s')\n", chan_type_short_name(chan_type[0]));
printf(" -M --measure-speed\n");
- printf(" Measures clock speed. See documentation!\n");
+ printf(" Measures clock speed. THIS IS REQUIRED! See documentation!\n");
printf(" -S --clock-speed <rx ppm>,<tx ppm>\n");
printf(" Correct speed of sound card's clock. Use '-M' to measure speed for\n");
printf(" some hours after temperature has settled. The use these results to\n");
printf(" correct signal processing speed. After adjustment, the clock must match\n");
- printf(" +- 1ppm or better. See documentation on how to measure correct value.\n");
+ printf(" +- 1ppm or better. CORRECTING CLOCK SPEED IS REQUIRED! See\n");
+ printf(" documentation on how to measure correct value.\n");
printf(" -F --flip-polarity\n");
printf(" Adjust, so the transmitter increases frequency, when positive levels\n");
printf(" are sent to sound device\n");
@@ -74,9 +79,11 @@ void print_help(const char *arg0)
static int handle_options(int argc, char **argv)
{
int skip_args = 0;
+ int rc;
const char *p;
static struct option long_options_special[] = {
+ {"channel-type", 1, 0, 't'},
{"measure-speed", 0, 0, 'M'},
{"clock-speed", 1, 0, 'S'},
{"flip-polarity", 0, 0, 'F'},
@@ -86,7 +93,7 @@ static int handle_options(int argc, char **argv)
{0, 0, 0, 0}
};
- set_options_common("MS:FN:P:A", long_options_special);
+ set_options_common("t:MS:FN:P:A", long_options_special);
while (1) {
int option_index = 0, c;
@@ -97,6 +104,19 @@ static int handle_options(int argc, char **argv)
break;
switch (c) {
+ case 't':
+ if (!strcmp(optarg, "list")) {
+ cnetz_channel_list();
+ exit(0);
+ }
+ rc = cnetz_channel_by_short_name(optarg);
+ if (rc < 0) {
+ fprintf(stderr, "Error, channel type '%s' unknown. Please use '-t list' to get a list. I suggest to use the default.\n", optarg);
+ exit(0);
+ }
+ OPT_ARRAY(num_chan_type, chan_type, rc)
+ skip_args += 2;
+ break;
case 'M':
measure_speed = 1;
skip_args++;
@@ -148,6 +168,7 @@ int main(int argc, char *argv[])
int skip_args;
const char *station_id = "";
int mandatory = 0;
+ int i;
/* init common tones */
init_freiton();
@@ -161,10 +182,22 @@ int main(int argc, char *argv[])
if (argc > 1) {
}
- if (!kanal) {
+ if (!num_kanal) {
printf("No channel (\"Kanal\") is specified, I suggest channel %d.\n\n", CNETZ_OGK_KANAL);
mandatory = 1;
}
+ if (num_kanal == 1 && num_sounddev == 0)
+ num_sounddev = 1; /* use defualt */
+ if (num_kanal != num_sounddev) {
+ fprintf(stdout, "You need to specify as many sound devices as you have channels.\n");
+ exit(0);
+ }
+ if (num_kanal == 1 && num_chan_type == 0)
+ num_chan_type = 1; /* use defualt */
+ if (num_kanal != num_chan_type) {
+ fprintf(stdout, "You need to specify as many channel types as you have channels.\n");
+ exit(0);
+ }
if (!set_clock_speed && !measure_speed) {
printf("No clock speed given. You need to measure clock using '-M' and later correct clock using '-S <rx ppm>,<tx ppm>'. See documentation for help!\n\n");
@@ -199,15 +232,33 @@ int main(int argc, char *argv[])
goto fail;
}
- /* create transceiver instance */
- rc = cnetz_create(sounddev, samplerate, do_pre_emphasis, do_de_emphasis, write_wave, read_wave, kanal, auth, ms_power, measure_speed, clock_speed, deviation, noise, loopback);
- if (rc < 0) {
- fprintf(stderr, "Failed to create \"Sender\" instance. Quitting!\n");
+ /* check for mandatory OgK */
+ for (i = 0; i < num_kanal; i++) {
+ if (chan_type[i] == CHAN_TYPE_OGK || chan_type[i] == CHAN_TYPE_OGK_SPK)
+ break;
+ }
+ if (i == num_kanal) {
+ fprintf(stderr, "You must define at least one OgK (control) or OgK/SPK (control/speech) channel type. Quitting!\n");
goto fail;
}
- printf("Base station ready, please tune transmitter to %.3f MHz and receiver to %.3f MHz.\n", cnetz_kanal2freq(CNETZ_OGK_KANAL, 0), cnetz_kanal2freq(CNETZ_OGK_KANAL, 1));
- if (kanal != CNETZ_OGK_KANAL)
- printf("When switching to speech channel %d, be sure that transmitter switches to %.3f MHz and receiver to %.3f MHz. (using pilot signal)\n", kanal, cnetz_kanal2freq(kanal, 0), cnetz_kanal2freq(kanal, 1));
+
+ /* check for mandatory OgK */
+ for (i = 0; i < num_kanal; i++) {
+ if (chan_type[i] == CHAN_TYPE_SPK || chan_type[i] == CHAN_TYPE_OGK_SPK)
+ break;
+ }
+ if (i == num_kanal)
+ fprintf(stderr, "You did not define any SpK (speech) channel. You will not be able to make any call.\n");
+
+ /* create transceiver instance */
+ for (i = 0; i < num_kanal; i++) {
+ rc = cnetz_create(kanal[i], chan_type[i], sounddev[i], samplerate, cross_channels, auth, ms_power, (i == 0) ? measure_speed : 0, clock_speed, deviation, noise, do_pre_emphasis, do_de_emphasis, write_wave, read_wave, loopback);
+ if (rc < 0) {
+ fprintf(stderr, "Failed to create \"Sender\" instance. Quitting!\n");
+ goto fail;
+ }
+ printf("Base station on channel %d ready, please tune transmitter to %.3f MHz and receiver to %.3f MHz.\n", kanal[i], cnetz_kanal2freq(kanal[i], 0), cnetz_kanal2freq(kanal[i], 1));
+ }
signal(SIGINT,sighandler);
signal(SIGHUP,sighandler);
diff --git a/src/common/call.c b/src/common/call.c
index 9523912..3139634 100644
--- a/src/common/call.c
+++ b/src/common/call.c
@@ -552,7 +552,7 @@ int process_call(void)
return 0;
}
}
- count = sound_read(call.sound, samples, call.latspl);
+ count = sound_read(call.sound, samples, samples, call.latspl);
if (count < 0) {
PDEBUG(DSENDER, DEBUG_ERROR, "Failed to read from sound device (rc = %d)!\n", count);
if (count == -EPIPE)
diff --git a/src/common/main.h b/src/common/main.h
index 2914ce7..011dc1b 100644
--- a/src/common/main.h
+++ b/src/common/main.h
@@ -1,9 +1,12 @@
-extern int kanal;
-extern const char *sounddev;
+extern int num_kanal;
+extern int kanal[];
+extern int num_sounddev;
+extern const char *sounddev[];
extern const char *call_sounddev;
extern int samplerate;
extern int latency;
+extern int cross_channels;
extern int use_mncc_sock;
extern int send_patterns;
extern int loopback;
@@ -20,6 +23,14 @@ extern char *optstring;
void set_options_common(const char *optstring_special, struct option *long_options_special);
void opt_switch_common(int c, char *arg0, int *skip_args);
+#define OPT_ARRAY(num_name, name, value) \
+{ \
+ if (num_name == MAX_SENDER) { \
+ fprintf(stderr, "Too many channels defined!\n"); \
+ exit(0); \
+ } \
+ name[num_name++] = value; \
+}
extern int quit;
void sighandler(int sigset);
diff --git a/src/common/main_common.c b/src/common/main_common.c
index ab71324..d2943ba 100644
--- a/src/common/main_common.c
+++ b/src/common/main_common.c
@@ -25,13 +25,17 @@
#include <signal.h>
#include "main.h"
#include "debug.h"
+#include "../common/sender.h"
/* common settings */
-int kanal = 0;
-const char *sounddev = "hw:0,0";
+int num_kanal = 0;
+int kanal[MAX_SENDER];
+int num_sounddev = 0;
+const char *sounddev[MAX_SENDER] = { "hw:0,0" };
const char *call_sounddev = "";
int samplerate = 48000;
int latency = 50;
+int cross_channels = 0;
int use_mncc_sock = 0;
int send_patterns = 1;
int loopback = 0;
@@ -51,13 +55,16 @@ void print_help_common(const char *arg0, const char *ext_usage)
printf(" -D --debug <level>\n");
printf(" Debug level: 0 = debug | 1 = info | 2 = notice (default = '%d')\n", debuglevel);
printf(" -k --kanal <channel>\n");
- printf(" Channel number of \"Sender\" (default = '%d')\n", kanal);
+ printf(" Channel number of \"Sender\"\n");
printf(" -d --device hw:<card>,<device>\n");
- printf(" Sound card and device number (default = '%s')\n", sounddev);
+ printf(" Sound card and device number (default = '%s')\n", sounddev[0]);
printf(" -s --samplerate <rate>\n");
printf(" Sample rate of sound device (default = '%d')\n", samplerate);
printf(" -l --latency <delay>\n");
printf(" How many milliseconds processed in advance (default = '%d')\n", latency);
+ printf(" -x --cross\n");
+ printf(" Cross channels on sound card. 1st channel (right) is swapped with\n");
+ printf(" second channel (left)\n");
printf(" -m --mncc-sock\n");
printf(" Disable built-in call contol and offer socket (to LCR)\n");
printf(" -c --call-device hw:<card>,<device>\n");
@@ -88,6 +95,7 @@ static struct option long_options_common[] = {
{"call-device", 1, 0, 'c'},
{"samplerate", 1, 0, 's'},
{"latency", 1, 0, 'l'},
+ {"cross", 0, 0, 'x'},
{"mncc-sock", 0, 0, 'm'},
{"send-patterns", 0, 0, 'p'},
{"loopback", 1, 0, 'L'},
@@ -99,7 +107,7 @@ static struct option long_options_common[] = {
{0, 0, 0, 0}
};
-const char *optstring_common = "hD:k:d:s:c:l:mp:L:r:EeW:R:";
+const char *optstring_common = "hD:k:d:s:c:l:xmp:L:r:EeW:R:";
struct option *long_options;
char *optstring;
@@ -134,11 +142,11 @@ void opt_switch_common(int c, char *arg0, int *skip_args)
*skip_args += 2;
break;
case 'k':
- kanal = atoi(optarg);
+ OPT_ARRAY(num_kanal, kanal, atoi(optarg))
*skip_args += 2;
break;
case 'd':
- sounddev = strdup(optarg);
+ OPT_ARRAY(num_sounddev, sounddev, strdup(optarg))
*skip_args += 2;
break;
case 's':
@@ -153,6 +161,10 @@ void opt_switch_common(int c, char *arg0, int *skip_args)
latency = atoi(optarg);
*skip_args += 2;
break;
+ case 'x':
+ cross_channels = 1;
+ *skip_args += 1;
+ break;
case 'm':
use_mncc_sock = 1;
*skip_args += 1;
@@ -205,4 +217,3 @@ void sighandler(int sigset)
quit = 1;
}
-
diff --git a/src/common/sender.c b/src/common/sender.c
index 6b7a738..c8e9b60 100644
--- a/src/common/sender.c
+++ b/src/common/sender.c
@@ -33,13 +33,64 @@ static sender_t **sender_tailp = &sender_head;
int cant_recover = 0;
/* Init transceiver instance and link to list of transceivers. */
-int sender_create(sender_t *sender, const char *sounddev, int samplerate, int pre_emphasis, int de_emphasis, const char *write_wave, const char *read_wave, int kanal, int loopback, double loss_volume, int use_pilot_signal)
+int sender_create(sender_t *sender, int kanal, const char *sounddev, int samplerate, int cross_channels, int pre_emphasis, int de_emphasis, const char *write_wave, const char *read_wave, int loopback, double loss_volume, int use_pilot_signal)
{
+ sender_t *master;
int rc = 0;
PDEBUG(DSENDER, DEBUG_DEBUG, "Creating 'Sender' instance\n");
+ /* if we find a channel that uses the same device as we do,
+ * we will link us as slave to this master channel. then we
+ * receive and send audio via second channel of the device
+ * of the master channel.
+ */
+ for (master = sender_head; master; master = master->next) {
+ if (master->kanal == kanal) {
+ PDEBUG(DSENDER, DEBUG_ERROR, "Channel %d may not be defined for multiple transceivers!\n", kanal);
+ rc = -EIO;
+ goto error;
+ }
+ if (!strcmp(master->sounddev, sounddev))
+ break;
+ }
+ if (master) {
+ if (master->slave) {
+ PDEBUG(DSENDER, DEBUG_ERROR, "Sound device '%s' cannot be used for channel %d. It is already shared by channel %d and %d!\n", sounddev, kanal, master->kanal, master->slave->kanal);
+ rc = -EBUSY;
+ goto error;
+ }
+ if (!sound_is_stereo_capture(master->sound) || !sound_is_stereo_playback(master->sound)) {
+ PDEBUG(DSENDER, DEBUG_ERROR, "Sound device '%s' cannot be used for more than one channel, because one direction is mono!\n", sounddev);
+ rc = -EBUSY;
+ goto error;
+ }
+ if (master->use_pilot_signal >= 0) {
+ PDEBUG(DSENDER, DEBUG_ERROR, "Cannot share sound device with channel %d, because second channel is used for pilot signal!\n", master->kanal);
+ rc = -EBUSY;
+ goto error;
+ }
+ if (use_pilot_signal >= 0) {
+ PDEBUG(DSENDER, DEBUG_ERROR, "Cannot share sound device with channel %d, because we need a stereo channel for pilot signal!\n", master->kanal);
+ rc = -EBUSY;
+ goto error;
+ }
+ /* link us to a master */
+ master->slave = sender;
+ sender->master = master;
+ } else {
+ /* open own device */
+ sender->sound = sound_open(sounddev, samplerate);
+ if (!sender->sound) {
+ PDEBUG(DSENDER, DEBUG_ERROR, "No sound device!\n");
+
+ rc = -EIO;
+ goto error;
+ }
+ }
+
sender->samplerate = samplerate;
+ sender->cross_channels = cross_channels;
sender->pre_emphasis = pre_emphasis;
sender->de_emphasis = de_emphasis;
sender->kanal = kanal;
@@ -47,14 +98,7 @@ int sender_create(sender_t *sender, const char *sounddev, int samplerate, int pr
sender->loss_volume = loss_volume;
sender->use_pilot_signal = use_pilot_signal;
sender->pilotton_phaseshift = 1.0 / ((double)samplerate / 1000.0);
-
- sender->sound = sound_open(sounddev, samplerate);
- if (!sender->sound) {
- PDEBUG(DSENDER, DEBUG_ERROR, "No sound device!\n");
-
- rc = -EIO;
- goto error;
- }
+ strncpy(sender->sounddev, sounddev, sizeof(sender->sounddev) - 1);
rc = init_samplerate(&sender->srstate, samplerate);
if (rc < 0) {
@@ -105,10 +149,16 @@ void sender_destroy(sender_t *sender)
sender_tailp = &sender_head;
while (*sender_tailp) {
if (sender == *sender_tailp)
- *sender_tailp = sender->next;
- sender_tailp = &sender->next;
+ *sender_tailp = (*sender_tailp)->next;
+ else
+ sender_tailp = &((*sender_tailp)->next);
}
+ if (sender->slave)
+ sender->slave->master = NULL;
+ if (sender->master)
+ sender->master->slave = NULL;
+
if (sender->sound)
sound_close(sender->sound);
@@ -140,9 +190,10 @@ static void gen_pilotton(sender_t *sender, int16_t *samples, int length)
}
/* Handle audio streaming of one transceiver. */
-void process_sender(sender_t *sender, int *quit, int latspl)
+static void process_sender_audio(sender_t *sender, int *quit, int latspl)
{
- int16_t samples[latspl], pilot[latspl];
+ sender_t *slave = sender->slave;
+ int16_t samples[latspl], pilot[latspl], slave_samples[latspl];
int rc, count;
count = sound_get_inbuffer(sender->sound);
@@ -161,12 +212,36 @@ cant_recover:
}
if (count < latspl) {
count = latspl - count;
+ /* load TX data from audio loop or from sender instance */
if (sender->loopback == 3)
jitter_load(&sender->audio, samples, count);
else
sender_send(sender, samples, count);
+ /* internal loopback: loop back TX audio to RX */
+ if (sender->loopback == 1) {
+ if (sender->wave_rec.fp)
+ wave_write(&sender->wave_rec, samples, count);
+ sender_receive(sender, samples, count);
+ }
+ /* do pre emphasis towards radio, not wave_write */
if (sender->pre_emphasis)
pre_emphasis(&sender->estate, samples, count);
+ /* do above for audio slave, if set */
+ if (slave) {
+ if (slave->loopback == 3)
+ jitter_load(&slave->audio, slave_samples, count);
+ else
+ sender_send(slave, slave_samples, count);
+ /* internal loopback, if audio slave is set */
+ if (slave && slave->loopback == 1) {
+ if (slave->wave_rec.fp)
+ wave_write(&slave->wave_rec, slave_samples, count);
+ sender_receive(slave, slave_samples, count);
+ }
+ /* do pre emphasis towards radio, not wave_write */
+ if (slave->pre_emphasis)
+ pre_emphasis(&slave->estate, slave_samples, count);
+ }
switch (sender->use_pilot_signal) {
case 2:
/* tone if pilot signal is on */
@@ -174,7 +249,10 @@ cant_recover:
gen_pilotton(sender, pilot, count);
else
memset(pilot, 0, count << 1);
- rc = sound_write(sender->sound, samples, pilot, count);
+ if (!sender->cross_channels)
+ rc = sound_write(sender->sound, samples, pilot, count);
+ else
+ rc = sound_write(sender->sound, pilot, samples, count);
break;
case 1:
/* positive signal if pilot signal is on */
@@ -182,7 +260,10 @@ cant_recover:
memset(pilot, 127, count << 1);
else
memset(pilot, 128, count << 1);
- rc = sound_write(sender->sound, samples, pilot, count);
+ if (!sender->cross_channels)
+ rc = sound_write(sender->sound, samples, pilot, count);
+ else
+ rc = sound_write(sender->sound, pilot, samples, count);
break;
case 0:
/* negative signal if pilot signal is on */
@@ -190,10 +271,26 @@ cant_recover:
memset(pilot, 128, count << 1);
else
memset(pilot, 127, count << 1);
- rc = sound_write(sender->sound, samples, pilot, count);
+ if (!sender->cross_channels)
+ rc = sound_write(sender->sound, samples, pilot, count);
+ else
+ rc = sound_write(sender->sound, pilot, samples, count);
break;
default:
- rc = sound_write(sender->sound, samples, samples, count);
+ /* if audio slave is set, write audio of both sender instances */
+ if (slave) {
+ if (!sender->cross_channels)
+ rc = sound_write(sender->sound, samples, slave_samples, count);
+ else
+ rc = sound_write(sender->sound, slave_samples, samples, count);
+ }else {
+ /* use pilot tone buffer for silence */
+ memset(pilot, 0, count << 1);
+ if (!sender->cross_channels)
+ rc = sound_write(sender->sound, samples, pilot, count);
+ else
+ rc = sound_write(sender->sound, pilot, samples, count);
+ }
}
if (rc < 0) {
PDEBUG(DSENDER, DEBUG_ERROR, "Failed to write TX data to sound device (rc = %d)\n", rc);
@@ -204,14 +301,12 @@ cant_recover:
}
return;
}
- if (sender->loopback == 1) {
- if (sender->wave_rec.fp)
- wave_write(&sender->wave_rec, samples, count);
- sender_receive(sender, samples, count);
- }
}
- count = sound_read(sender->sound, samples, latspl);
+ if (!sender->cross_channels)
+ count = sound_read(sender->sound, samples, slave_samples, latspl);
+ else
+ count = sound_read(sender->sound, slave_samples, samples, latspl);
//printf("count=%d time= %.4f\n", count, (double)count * 1000 / sender->samplerate);
if (count < 0) {
PDEBUG(DSENDER, DEBUG_ERROR, "Failed to read from sound device (rc = %d)!\n", count);
@@ -223,6 +318,7 @@ cant_recover:
return;
}
if (count) {
+ /* do de emphasis from radio (then write_wave/wave_read), receive audio, process echo test */
if (sender->de_emphasis)
de_emphasis(&sender->estate, samples, count);
if (sender->wave_play.fp)
@@ -232,8 +328,21 @@ cant_recover:
wave_write(&sender->wave_rec, samples, count);
sender_receive(sender, samples, count);
}
- if (sender->loopback == 3) {
+ if (sender->loopback == 3)
jitter_save(&sender->audio, samples, count);
+ /* do above for audio slave, if set */
+ if (slave) {
+ if (slave->de_emphasis)
+ de_emphasis(&slave->estate, slave_samples, count);
+ if (slave->wave_play.fp)
+ wave_read(&slave->wave_play, slave_samples, count);
+ if (slave->loopback != 1) {
+ if (slave->wave_rec.fp)
+ wave_write(&slave->wave_rec, slave_samples, count);
+ sender_receive(slave, slave_samples, count);
+ }
+ if (slave->loopback == 3)
+ jitter_save(&slave->audio, slave_samples, count);
}
}
}
@@ -247,11 +356,12 @@ void main_loop(int *quit, int latency)
while(!(*quit)) {
/* process sound of all transceivers */
- sender = sender_head;
- while (sender) {
+ for (sender = sender_head; sender; sender = sender->next) {
+ /* do not process audio for an audio slave, since it is done by audio master */
+ if (sender->master) /* if master is set, we are an audio slave */
+ continue;
latspl = sender->samplerate * latency / 1000;
- process_sender(sender, quit, latspl);
- sender = sender->next;
+ process_sender_audio(sender, quit, latspl);
}
/* process timers */
diff --git a/src/common/sender.h b/src/common/sender.h
index 7e0eb74..db0936e 100644
--- a/src/common/sender.h
+++ b/src/common/sender.h
@@ -5,9 +5,13 @@
#include "loss.h"
#include "emphasis.h"
+#define MAX_SENDER 16
+
/* common structure of each transmitter */
typedef struct sender {
struct sender *next;
+ struct sender *slave; /* points to audio device slave member */
+ struct sender *master; /* points to audio device master source */
/* call reference */
int callref;
@@ -17,7 +21,9 @@ typedef struct sender {
/* sound */
void *sound;
+ char sounddev[64]; /* sound device name */
int samplerate;
+ int cross_channels; /* swap right and left on IO */
samplerate_t srstate; /* sample rate conversion state */
int pre_emphasis; /* use pre_emhasis, done by sender */
int de_emphasis; /* use de_emhasis, done by sender */
@@ -52,7 +58,7 @@ typedef struct sender {
extern sender_t *sender_head;
extern int cant_recover;
-int sender_create(sender_t *sender, const char *sounddev, int samplerate, int pre_emphasis, int de_emphasis, const char *write_wave, const char *read_wave, int kanal, int loopback, double loss_volume, int use_pilot_signal);
+int sender_create(sender_t *sender, int kanal, const char *sounddev, int samplerate, int cross_channels, int pre_emphasis, int de_emphasis, const char *write_wave, const char *read_wave, int loopback, double loss_volume, int use_pilot_signal);
void sender_destroy(sender_t *sender);
void sender_send(sender_t *sender, int16_t *samples, int count);
void sender_receive(sender_t *sender, int16_t *samples, int count);
diff --git a/src/common/sound.h b/src/common/sound.h
index 6685d45..82dc17a 100644
--- a/src/common/sound.h
+++ b/src/common/sound.h
@@ -2,5 +2,8 @@
void *sound_open(const char *device, int samplerate);
void sound_close(void *inst);
int sound_write(void *inst, int16_t *samples_left, int16_t *samples_right, int num);
-int sound_read(void *inst, int16_t *samples, int num);
+int sound_read(void *inst, int16_t *samples_left, int16_t *samples_right, int num);
int sound_get_inbuffer(void *inst);
+int sound_is_stereo_capture(void *inst);
+int sound_is_stereo_playback(void *inst);
+
diff --git a/src/common/sound_alsa.c b/src/common/sound_alsa.c
index 0ace241..9a35e3e 100644
--- a/src/common/sound_alsa.c
+++ b/src/common/sound_alsa.c
@@ -203,18 +203,17 @@ int sound_write(void *inst, int16_t *samples_left, int16_t *samples_right, int n
return rc;
}
-int sound_read(void *inst, int16_t *samples, int num)
+int sound_read(void *inst, int16_t *samples_left, int16_t *samples_right, int num)
{
sound_t *sound = (sound_t *)inst;
int16_t buff[num << 1];
- int32_t s32;
int rc;
int i, ii;
if (sound->cchannels == 2)
rc = snd_pcm_readi(sound->chandle, buff, num);
else
- rc = snd_pcm_readi(sound->chandle, samples, num);
+ rc = snd_pcm_readi(sound->chandle, samples_left, num);
if (rc < 0) {
if (errno == EAGAIN)
@@ -228,11 +227,11 @@ int sound_read(void *inst, int16_t *samples, int num)
if (sound->cchannels == 2) {
for (i = 0, ii = 0; i < rc; i++) {
- s32 = buff[ii++];
- s32 += buff[ii++];
- *samples++ = s32 >> 1;
+ *samples_right++ = buff[ii++];
+ *samples_left++ = buff[ii++];
}
- }
+ } else
+ memcpy(samples_right, samples_left, num << 1);
return rc;
}
@@ -261,3 +260,21 @@ int sound_get_inbuffer(void *inst)
return delay;
}
+int sound_is_stereo_capture(void *inst)
+{
+ sound_t *sound = (sound_t *)inst;
+
+ if (sound->cchannels == 2)
+ return 1;
+ return 0;
+}
+
+int sound_is_stereo_playback(void *inst)
+{
+ sound_t *sound = (sound_t *)inst;
+
+ if (sound->pchannels == 2)
+ return 1;
+ return 0;
+}
+
diff --git a/src/nmt/main.c b/src/nmt/main.c
index 8503bf3..90c4b16 100644
--- a/src/nmt/main.c
+++ b/src/nmt/main.c
@@ -37,7 +37,8 @@
#include "announcement.h"
/* settings */
-enum nmt_chan_type chan_type = CHAN_TYPE_CC_TC;
+int num_chan_type = 0;
+enum nmt_chan_type chan_type[MAX_SENDER] = { CHAN_TYPE_CC_TC };
int ms_power = 1; /* 1..3 */
char traffic_area[3] = "";
char area_no = 0;
@@ -49,7 +50,7 @@ void print_help(const char *arg0)
print_help_common(arg0, "-y <traffic area> | list ");
/* - - */
printf(" -t --channel-type <channel type> | list\n");
- printf(" Give channel type, use 'list' to get a list. (default = '%s')\n", chan_type_short_name(chan_type));
+ printf(" Give channel type, use 'list' to get a list. (default = '%s')\n", chan_type_short_name(chan_type[0]));
printf(" -P --ms-power <power level>\n");
printf(" Give power level of the mobile station 1..3. (default = '%d')\n", ms_power);
printf(" 3 = 15 W / 7 W (handheld), 2 = 1.5 W, 1 = 150 mW\n");
@@ -99,7 +100,6 @@ static int handle_options(int argc, char **argv)
switch (c) {
case 't':
-
if (!strcmp(optarg, "list")) {
nmt_channel_list();
exit(0);
@@ -109,7 +109,7 @@ static int handle_options(int argc, char **argv)
fprintf(stderr, "Error, channel type '%s' unknown. Please use '-t list' to get a list. I suggest to use the default.\n", optarg);
exit(0);
}
- chan_type = rc;
+ OPT_ARRAY(num_chan_type, chan_type, rc)
skip_args += 2;
break;
case 'P':
@@ -192,6 +192,7 @@ int main(int argc, char *argv[])
int skip_args;
const char *station_id = "";
int mandatory = 0;
+ int i;
/* init common tones */
init_nmt_tones();
@@ -204,10 +205,22 @@ int main(int argc, char *argv[])
if (argc > 1)
station_id = argv[1];
- if (!kanal) {
+ if (!num_kanal) {
printf("No channel (\"Kanal\") is specified, I suggest channel 1 (-k 1).\n\n");
mandatory = 1;
}
+ if (num_kanal == 1 && num_sounddev == 0)
+ num_sounddev = 1; /* use defualt */
+ if (num_kanal != num_sounddev) {
+ fprintf(stdout, "You need to specify as many sound devices as you have channels.\n");
+ exit(0);
+ }
+ if (num_kanal == 1 && num_chan_type == 0)
+ num_chan_type = 1; /* use defualt */
+ if (num_kanal != num_chan_type) {
+ fprintf(stdout, "You need to specify as many channel types as you have channels.\n");
+ exit(0);
+ }
if (!traffic_area[0]) {
printf("No traffic area is specified, I suggest to use Sweden (-y SE,1) and set the phone's roaming to 'SE' also.\n\n");
@@ -239,19 +252,17 @@ int main(int argc, char *argv[])
}
/* create transceiver instance */
- rc = nmt_create(sounddev, samplerate, do_pre_emphasis, do_de_emphasis, write_wave, read_wave, kanal, (loopback) ? CHAN_TYPE_TEST : chan_type, ms_power, nmt_digits2value(traffic_area, 2), area_no, compander, supervisory, loopback);
- if (rc < 0) {
- fprintf(stderr, "Failed to create transceiver instance. Quitting!\n");
- goto fail;
- }
- if (kanal > 200) {
- printf("Base station ready, please tune transmitter to %.4f MHz and receiver "
- "to %.4f MHz.\n", nmt_channel2freq(kanal, 0),
- nmt_channel2freq(kanal, 1));
- } else {
- printf("Base station ready, please tune transmitter to %.3f MHz and receiver "
- "to %.3f MHz.\n", nmt_channel2freq(kanal, 0),
- nmt_channel2freq(kanal, 1));
+ for (i = 0; i < num_kanal; i++) {
+ rc = nmt_create(kanal[i], (loopback) ? CHAN_TYPE_TEST : chan_type[i], sounddev[i], samplerate, cross_channels, do_pre_emphasis, do_de_emphasis, write_wave, read_wave, ms_power, nmt_digits2value(traffic_area, 2), area_no, compander, supervisory, loopback);
+ if (rc < 0) {
+ fprintf(stderr, "Failed to create transceiver instance. Quitting!\n");
+ goto fail;
+ }
+ if (kanal[i] > 200) {
+ printf("Base station on channel %d ready, please tune transmitter to %.4f MHz and receiver to %.4f MHz.\n", kanal[i], nmt_channel2freq(kanal[i], 0), nmt_channel2freq(kanal[i], 1));
+ } else {
+ printf("Base station on channel %d ready, please tune transmitter to %.3f MHz and receiver to %.3f MHz.\n", kanal[i], nmt_channel2freq(kanal[i], 0), nmt_channel2freq(kanal[i], 1));
+ }
}
signal(SIGINT,sighandler);
diff --git a/src/nmt/nmt.c b/src/nmt/nmt.c
index 4249f48..9c42a3c 100644
--- a/src/nmt/nmt.c
+++ b/src/nmt/nmt.c
@@ -283,7 +283,7 @@ static void nmt_timeout(struct timer *timer);
static void nmt_go_idle(nmt_t *nmt);
/* Create transceiver instance and link to a list. */
-int nmt_create(const char *sounddev, int samplerate, int pre_emphasis, int de_emphasis, const char *write_wave, const char *read_wave, int channel, enum nmt_chan_type chan_type, uint8_t ms_power, uint8_t traffic_area, uint8_t area_no, int compander, int supervisory, int loopback)
+int nmt_create(int channel, enum nmt_chan_type chan_type, const char *sounddev, int samplerate, int cross_channels, int pre_emphasis, int de_emphasis, const char *write_wave, const char *read_wave, uint8_t ms_power, uint8_t traffic_area, uint8_t area_no, int compander, int supervisory, int loopback)
{
nmt_t *nmt;
int rc;
@@ -303,12 +303,12 @@ int nmt_create(const char *sounddev, int samplerate, int pre_emphasis, int de_em
if (chan_type == CHAN_TYPE_CC) {
PDEBUG(DNMT, DEBUG_NOTICE, "*** Selected channel can be used for calling only.\n");
- PDEBUG(DNMT, DEBUG_NOTICE, "*** No call from the mobile phone is possible.\n");
+ PDEBUG(DNMT, DEBUG_NOTICE, "*** No call from the mobile phone is possible on this channel.\n");
PDEBUG(DNMT, DEBUG_NOTICE, "*** Use combined 'CC/TC' instead!\n");
}
if (chan_type == CHAN_TYPE_TC) {
PDEBUG(DNMT, DEBUG_NOTICE, "*** Selected channel can be used for traffic only.\n");
- PDEBUG(DNMT, DEBUG_NOTICE, "*** No call to the mobile phone is possible.\n");
+ PDEBUG(DNMT, DEBUG_NOTICE, "*** No call to the mobile phone is possible on this channel.\n");
PDEBUG(DNMT, DEBUG_NOTICE, "*** Use combined 'CC/TC' instead!\n");
}
if (chan_type == CHAN_TYPE_TEST && !loopback) {
@@ -324,7 +324,7 @@ int nmt_create(const char *sounddev, int samplerate, int pre_emphasis, int de_em
PDEBUG(DNMT, DEBUG_DEBUG, "Creating 'NMT' instance for channel = %d (sample rate %d).\n", channel, samplerate);
/* init general part of transceiver */
- rc = sender_create(&nmt->sender, sounddev, samplerate, pre_emphasis, de_emphasis, write_wave, read_wave, channel, loopback, 0, -1);
+ rc = sender_create(&nmt->sender, channel, sounddev, samplerate, cross_channels, pre_emphasis, de_emphasis, write_wave, read_wave, loopback, 0, -1);
if (rc < 0) {
PDEBUG(DNMT, DEBUG_ERROR, "Failed to init transceiver process!\n");
goto error;
@@ -372,6 +372,7 @@ void nmt_destroy(sender_t *sender)
static void nmt_go_idle(nmt_t *nmt)
{
timer_stop(&nmt->timer);
+ nmt->page_for_nmt = NULL;
PDEBUG(DNMT, DEBUG_INFO, "Entering IDLE state, sending idle frames on %s.\n", chan_type_long_name(nmt->sysinfo.chan_type));
nmt_new_state(nmt, STATE_IDLE);
@@ -395,13 +396,34 @@ static void nmt_release(nmt_t *nmt)
/* Enter paging state and transmit phone's number on calling channel */
static void nmt_page(nmt_t *nmt, char ms_country, const char *ms_number, int try)
{
+ sender_t *sender;
+ nmt_t *other;
+
PDEBUG(DNMT, DEBUG_INFO, "Entering paging state (try %d), sending call to '%c,%s'.\n", try, ms_country, ms_number);
nmt->subscriber.country = ms_country;
strcpy(nmt->subscriber.number, ms_number);
nmt->page_try = try;
- nmt_new_state(nmt, STATE_MT_PAGING);
- nmt_set_dsp_mode(nmt, DSP_MODE_FRAME);
- nmt->tx_frame_count = 0;
+ /* page on all CC (CC/TC) */
+ for (sender = sender_head; sender; sender = sender->next) {
+ other = (nmt_t *)sender;
+ if (nmt->sysinfo.chan_type != CHAN_TYPE_CC
+ && nmt->sysinfo.chan_type != CHAN_TYPE_CC_TC)
+ continue;
+ if (nmt->state != STATE_IDLE)
+ continue;
+ if (other == nmt) {
+ /* this is us */
+ PDEBUG(DNMT, DEBUG_INFO, "Paging on our channel %d.\n", other->sender.kanal);
+ } else {
+ /* this is not us */
+ PDEBUG(DNMT, DEBUG_INFO, "Paging on other channel %d.\n", other->sender.kanal);
+ other->page_for_nmt = nmt;
+ }
+ nmt_new_state(other, STATE_MT_PAGING);
+ nmt_set_dsp_mode(other, DSP_MODE_FRAME);
+ other->tx_frame_count = 0;
+ other->mt_channel = nmt->sender.kanal; /* ! channel from us (nmt->...) */
+ }
}
/*
@@ -801,6 +823,11 @@ static void tx_mt_paging(nmt_t *nmt, frame_t *frame)
tx_idle(nmt, frame);
/* wait some time to get answer. use more than one frame due to delay of audio processing */
if (nmt->tx_frame_count == 5) {
+ /* if we page for different channel, we go idle on timeout */
+ if (nmt->page_for_nmt) {
+ nmt_go_idle(nmt);
+ return;
+ }
PDEBUG(DNMT, DEBUG_NOTICE, "No answer from mobile phone (try %d).\n", nmt->page_try);
if (nmt->page_try == PAGE_TRIES) {
PDEBUG(DNMT, DEBUG_INFO, "Release call towards network.\n");
@@ -821,9 +848,16 @@ static void rx_mt_paging(nmt_t *nmt, frame_t *frame)
break;
if (!match_subscriber(nmt, frame))
break;
- PDEBUG(DNMT, DEBUG_INFO, "Received call acknowledgement.\n");
+ PDEBUG(DNMT, DEBUG_INFO, "Received call acknowledgement on channel %d.\n", nmt->sender.kanal);
nmt_new_state(nmt, STATE_MT_CHANNEL);
nmt->tx_frame_count = 0;
+ if (nmt->page_for_nmt) {
+ PDEBUG(DNMT, DEBUG_INFO, " -> Notify initiating channel %d about this ack.\n", nmt->mt_channel);
+ /* we just send frame 2b on initiating channel, but this is ignored by the phone anyway.
+ * it would be correct to send frame 6 on initiating channel. */
+ nmt_new_state(nmt->page_for_nmt, STATE_MT_CHANNEL);
+ nmt->page_for_nmt->tx_frame_count = 0;
+ }
break;
default:
PDEBUG(DNMT, DEBUG_DEBUG, "Dropping message %s in state %s\n", nmt_frame_name(frame->index), nmt_state_name(nmt->state));
@@ -837,8 +871,13 @@ static void tx_mt_channel(nmt_t *nmt, frame_t *frame)
frame->traffic_area = nmt->sysinfo.traffic_area;
frame->ms_country = nmt_digits2value(&nmt->subscriber.country, 1);
frame->ms_number = nmt_digits2value(nmt->subscriber.number, 6);
- frame->tc_no = nmt_encode_channel(nmt->sender.kanal, nmt->sysinfo.ms_power);
+ frame->tc_no = nmt_encode_channel(nmt->mt_channel, nmt->sysinfo.ms_power);
PDEBUG(DNMT, DEBUG_INFO, "Send channel activation to mobile.\n");
+ /* after assigning for differnet channel, we go idle. */
+ if (nmt->page_for_nmt) {
+ nmt_go_idle(nmt);
+ return;
+ }
nmt_new_state(nmt, STATE_MT_IDENT);
}
diff --git a/src/nmt/nmt.h b/src/nmt/nmt.h
index 2d6f08d..f6401d3 100644
--- a/src/nmt/nmt.h
+++ b/src/nmt/nmt.h
@@ -85,6 +85,10 @@ typedef struct nmt {
char dialing[33]; /* dialed digits */
int page_try; /* number of paging try */
+ /* special state for paging on different CC */
+ struct nmt *page_for_nmt; /* only page and assign channel for nmt instance as set */
+ int mt_channel; /* channel to use */
+
/* features */
int compander; /* if compander shall be used */
int supervisory; /* if set, use supervisory signal 1..4 */
@@ -134,7 +138,7 @@ const char *chan_type_long_name(enum nmt_chan_type chan_type);
double nmt_channel2freq(int channel, int uplink);
void nmt_country_list(void);
uint8_t nmt_country_by_short_name(const char *short_name);
-int nmt_create(const char *sounddev, int samplerate, int pre_emphasis, int de_emphasis, const char *write_wave, const char *read_wave, int channel, enum nmt_chan_type chan_type, uint8_t ms_power, uint8_t traffic_area, uint8_t area_no, int compander, int supervisory, int loopback);
+int nmt_create(int channel, enum nmt_chan_type chan_type, const char *sounddev, int samplerate, int cross_channels, int pre_emphasis, int de_emphasis, const char *write_wave, const char *read_wave, uint8_t ms_power, uint8_t traffic_area, uint8_t area_no, int compander, int supervisory, int loopback);
void nmt_destroy(sender_t *sender);
void nmt_receive_frame(nmt_t *nmt, const char *bits, double quality, double level, double frames_elapsed);
const char *nmt_get_frame(nmt_t *nmt);