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:
@@ -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);
|
||||
|
Reference in New Issue
Block a user