Multi transceiver support
This can be multiple transceivers on multiple sound cards. Two transceivers can be bundled on one sound device as well, using both channels.
This commit is contained in:
@@ -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;
|
||||
|
@@ -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);
|
||||
|
@@ -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);
|
||||
|
@@ -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;
|
||||
|
@@ -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);
|
||||
|
@@ -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);
|
||||
|
@@ -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;
|
||||
|
@@ -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);
|
||||
|
@@ -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
|
||||
}
|
||||
|
||||
|
@@ -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);
|
||||
|
@@ -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)
|
||||
|
@@ -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);
|
||||
|
@@ -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;
|
||||
}
|
||||
|
||||
|
||||
|
@@ -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 */
|
||||
|
@@ -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);
|
||||
|
@@ -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);
|
||||
|
||||
|
@@ -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;
|
||||
}
|
||||
|
||||
|
@@ -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);
|
||||
|
@@ -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);
|
||||
}
|
||||
|
||||
|
@@ -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);
|
||||
|
Reference in New Issue
Block a user