diff options
-rw-r--r-- | src/anetz/anetz.c | 4 | ||||
-rw-r--r-- | src/anetz/anetz.h | 2 | ||||
-rw-r--r-- | src/anetz/main.c | 23 | ||||
-rw-r--r-- | src/bnetz/bnetz.c | 4 | ||||
-rw-r--r-- | src/bnetz/bnetz.h | 2 | ||||
-rw-r--r-- | src/bnetz/main.c | 40 | ||||
-rw-r--r-- | src/cnetz/cnetz.c | 282 | ||||
-rw-r--r-- | src/cnetz/cnetz.h | 18 | ||||
-rw-r--r-- | src/cnetz/dsp.c | 58 | ||||
-rw-r--r-- | src/cnetz/main.c | 73 | ||||
-rw-r--r-- | src/common/call.c | 2 | ||||
-rw-r--r-- | src/common/main.h | 15 | ||||
-rw-r--r-- | src/common/main_common.c | 27 | ||||
-rw-r--r-- | src/common/sender.c | 166 | ||||
-rw-r--r-- | src/common/sender.h | 8 | ||||
-rw-r--r-- | src/common/sound.h | 5 | ||||
-rw-r--r-- | src/common/sound_alsa.c | 31 | ||||
-rw-r--r-- | src/nmt/main.c | 47 | ||||
-rw-r--r-- | src/nmt/nmt.c | 57 | ||||
-rw-r--r-- | src/nmt/nmt.h | 6 |
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); |