C-Netz: Work on system informations, metering and call queues

- Make system informations configurable
- Simulate call mertering
- Support for call queues on busy voice channels
This commit is contained in:
Andreas Eversberg 2017-08-26 17:48:13 +02:00
parent 3bfba37fff
commit dfdad21e3f
11 changed files with 573 additions and 192 deletions

View File

@ -311,7 +311,7 @@ dsp.c:218 notice : Clock: RX=19.44 TX=19.49; Signal: RX=19.35 TX=19.43 ppm
<p>
One can see that the measured clock from the sound card (sample rate) differs about +19.45 PPM.
Because I did not do any correction to the signal processing, the same error applies to the signal processing.
Use command line option '-S 19.45,19.45' or '--clock-speed 19.45,19.45' to compensate the clock error by adjusting the signal processor.
Use command line option '-C 19.45,19.45' or '--clock-speed 19.45,19.45' to compensate the clock error by adjusting the signal processor.
Let it run again for one hour and you will see that clock deviation of the signal is below 1 PPM.
</p>
@ -384,7 +384,7 @@ To run the base station I the following command line options:
<pre>
# cnetz -k 131 -p -d -S 19.25,19.25
# cnetz -k 131 -p -d -C 19.25,19.25
...
cnetz.c:142 notice : You selected channel 131 ('Orga-Kanal') for combined callin
g + traffic channel. Some phones will reject this.

View File

@ -39,6 +39,12 @@
* created. The transaction is linked to OgK. When the scheduler schedules
* VAK(R), the SpK is allocated and the transaction is linked to it.
*
* If no SpK is available, the call is rejected. If queue (Warteschlange) is
* enabled, WSK(R) is scheduled. After transmission, the state changes to
* TRANS_MT_QUEUE. Upon timeout (no channel becomes available), the call is
* rejected by scheduling VA(R). Upon available channel the call proceeeds with
* VAK(R) as described above.
*
* If an MO (mobile originating) call is made (received VWG(K)), a transaction
* with callref is created. The transaction is linked to OgK. When the
* scheduler schedules WAF(M), the process waits for WUE(M). If not received,
@ -47,6 +53,12 @@
* then schedules VAG(R), the SpK is allocated and the transaction is linked to
* it.
*
* If no SpK is available, the call is rejected by scheduling WBN(R). If queue
* (Warteschlange) is enabled, WWBP(R) is scheduled. After transmission, the
* state is changed to TRANS_MO_QUEUE. Upon timeout (no channel becomes
* available), the call is rejected by scheduling VA(R). Upon available channel
* the call proceeeds with VAG(R) as described above.
*
* Switching to SpK is performed two time slots after transmitting VAK(R) or
* VAG(R). The timer is started. The schedulers schedules 8 times BQ(K) and
* awaits at least one BEL(K). If BEK(K) is received, the timer is stoped. If
@ -129,6 +141,7 @@
#include <string.h>
#include <errno.h>
#include <math.h>
#include <inttypes.h>
#include "../common/sample.h"
#include "../common/debug.h"
#include "../common/timer.h"
@ -231,7 +244,7 @@ int cnetz_init(void)
}
/* Create transceiver instance and link to a list. */
int cnetz_create(int kanal, enum cnetz_chan_type chan_type, const char *audiodev, int use_sdr, enum demod_type demod, int samplerate, double rx_gain, int auth, int ms_power, int measure_speed, double clock_speed[2], int polarity, int pre_emphasis, int de_emphasis, const char *write_rx_wave, const char *write_tx_wave, const char *read_rx_wave, const char *read_tx_wave, int loopback)
int cnetz_create(int kanal, enum cnetz_chan_type chan_type, const char *audiodev, int use_sdr, enum demod_type demod, int samplerate, double rx_gain, int auth, int warteschlange, int metering, int ms_power, int measure_speed, double clock_speed[2], int polarity, int pre_emphasis, int de_emphasis, const char *write_rx_wave, const char *write_tx_wave, const char *read_rx_wave, const char *read_tx_wave, int loopback)
{
sender_t *sender;
cnetz_t *cnetz;
@ -313,6 +326,8 @@ int cnetz_create(int kanal, enum cnetz_chan_type chan_type, const char *audiodev
cnetz->chan_type = chan_type;
cnetz->auth = auth;
cnetz->warteschlange = warteschlange;
cnetz->metering = metering;
cnetz->ms_power = ms_power;
switch (polarity) {
@ -409,63 +424,7 @@ void cnetz_destroy(sender_t *sender)
free(cnetz);
}
/* Abort connection, if any and send idle broadcast */
void cnetz_go_idle(cnetz_t *cnetz)
{
if (cnetz->state == CNETZ_IDLE)
return;
if (cnetz->trans_list) {
PDEBUG(DCNETZ, DEBUG_ERROR, "Releasing but still having transaction, please fix!\n");
if (cnetz->trans_list->callref)
call_in_release(cnetz->trans_list->callref, CAUSE_NORMAL);
destroy_transaction(cnetz->trans_list);
}
PDEBUG(DCNETZ, DEBUG_INFO, "Entering IDLE state on channel %d.\n", cnetz->sender.kanal);
cnetz_new_state(cnetz, CNETZ_IDLE);
/* set scheduler to OgK or turn off SpK */
if (cnetz->dsp_mode == DSP_MODE_SPK_K || cnetz->dsp_mode == DSP_MODE_SPK_V) {
/* go idle after next frame/slot */
cnetz_set_sched_dsp_mode(cnetz, (cnetz->sender.kanal == CNETZ_OGK_KANAL) ? DSP_MODE_OGK : DSP_MODE_OFF, 1);
} else {
cnetz_set_sched_dsp_mode(cnetz, (cnetz->sender.kanal == CNETZ_OGK_KANAL) ? DSP_MODE_OGK : DSP_MODE_OFF, 0);
cnetz_set_dsp_mode(cnetz, (cnetz->sender.kanal == CNETZ_OGK_KANAL) ? DSP_MODE_OGK : DSP_MODE_OFF);
}
}
/* Initiate release connection on speech channel */
static void cnetz_release(transaction_t *trans, uint8_t cause)
{
trans_new_state(trans, TRANS_AF);
trans->repeat = 0;
trans->release_cause = cause;
trans->cnetz->sched_switch_mode = 0;
timer_stop(&trans->timer);
}
/* Receive audio from call instance. */
void call_rx_audio(int callref, sample_t *samples, int count)
{
sender_t *sender;
cnetz_t *cnetz;
for (sender = sender_head; sender; sender = sender->next) {
cnetz = (cnetz_t *) sender;
if (cnetz->trans_list && cnetz->trans_list->callref == callref)
break;
}
if (!sender)
return;
if (cnetz->dsp_mode == DSP_MODE_SPK_V) {
/* store as is, since we convert rate when processing FSK frames */
jitter_save(&cnetz->sender.dejitter, samples, count);
}
}
cnetz_t *search_free_spk(int extended)
static cnetz_t *search_free_spk(int extended)
{
sender_t *sender;
cnetz_t *cnetz, *ogk_spk = NULL;
@ -493,7 +452,7 @@ cnetz_t *search_free_spk(int extended)
return ogk_spk;
}
cnetz_t *search_ogk(void)
static cnetz_t *search_ogk(void)
{
sender_t *sender;
cnetz_t *cnetz;
@ -512,10 +471,80 @@ cnetz_t *search_ogk(void)
return NULL;
}
int call_out_setup(int callref, const char __attribute__((unused)) *caller_id, enum number_type __attribute__((unused)) caller_type, const char *dialing)
/* Abort connection, if any and send idle broadcast */
void cnetz_go_idle(cnetz_t *cnetz)
{
cnetz_t *ogk;
transaction_t *trans;
if (cnetz->state == CNETZ_IDLE)
return;
if (cnetz->trans_list) {
PDEBUG(DCNETZ, DEBUG_ERROR, "Releasing but still having transaction, please fix!\n");
if (cnetz->trans_list->callref)
call_in_release(cnetz->trans_list->callref, CAUSE_NORMAL);
destroy_transaction(cnetz->trans_list);
}
PDEBUG(DCNETZ, DEBUG_INFO, "Entering IDLE state on channel %d.\n", cnetz->sender.kanal);
cnetz_new_state(cnetz, CNETZ_IDLE);
/* set scheduler to OgK or turn off SpK */
if (cnetz->dsp_mode == DSP_MODE_SPK_K || cnetz->dsp_mode == DSP_MODE_SPK_V) {
/* go idle after next frame/slot */
cnetz_set_sched_dsp_mode(cnetz, (cnetz->sender.kanal == CNETZ_OGK_KANAL) ? DSP_MODE_OGK : DSP_MODE_OFF, 1);
} else {
cnetz_set_sched_dsp_mode(cnetz, (cnetz->sender.kanal == CNETZ_OGK_KANAL) ? DSP_MODE_OGK : DSP_MODE_OFF, 0);
cnetz_set_dsp_mode(cnetz, (cnetz->sender.kanal == CNETZ_OGK_KANAL) ? DSP_MODE_OGK : DSP_MODE_OFF);
}
/* check for first phone in queue and trigger completion of call (becoming idle means that SpK is now available) */
ogk = search_ogk();
trans = search_transaction(ogk, TRANS_MT_QUEUE | TRANS_MO_QUEUE);
if (trans) {
PDEBUG(DCNETZ, DEBUG_NOTICE, "Now channel available for queued subscriber '%s'.\n", transaction2rufnummer(trans));
trans_new_state(trans, (trans->state == TRANS_MT_QUEUE) ? TRANS_MT_DELAY : TRANS_MO_DELAY);
timer_stop(&trans->timer);
timer_start(&trans->timer, 2.0);
}
}
/* Initiate release connection on speech channel */
static void cnetz_release(transaction_t *trans, uint8_t cause)
{
trans_new_state(trans, (trans->cnetz->dsp_mode == DSP_MODE_OGK) ? TRANS_VA : TRANS_AF);
trans->repeat = 0;
trans->release_cause = cause;
trans->cnetz->sched_switch_mode = 0;
timer_stop(&trans->timer);
}
/* Receive audio from call instance. */
void call_rx_audio(int callref, sample_t *samples, int count)
{
sender_t *sender;
cnetz_t *cnetz;
for (sender = sender_head; sender; sender = sender->next) {
cnetz = (cnetz_t *) sender;
if (cnetz->trans_list && cnetz->trans_list->callref == callref)
break;
}
if (!sender)
return;
if (cnetz->dsp_mode == DSP_MODE_SPK_V) {
/* store as is, since we convert rate when processing FSK frames */
jitter_save(&cnetz->sender.dejitter, samples, count);
}
}
int call_out_setup(int callref, const char __attribute__((unused)) *caller_id, enum number_type __attribute__((unused)) caller_type, const char *dialing)
{
sender_t *sender;
cnetz_t *cnetz, *spk;
int extended;
transaction_t *trans;
uint8_t futln_nat;
uint8_t futln_fuvst;
@ -544,7 +573,8 @@ inval:
futln_rest = atoi(dialing + 2);
/* 2. check if the subscriber is attached */
if (!find_db(futln_nat, futln_fuvst, futln_rest)) {
extended = find_db(futln_nat, futln_fuvst, futln_rest);
if (extended < 0) {
PDEBUG(DCNETZ, DEBUG_NOTICE, "Outgoing call to not attached subscriber, rejecting!\n");
return -CAUSE_OUTOFORDER;
}
@ -562,23 +592,27 @@ inval:
return -CAUSE_BUSY;
}
/* 4. check if all senders are busy, return NOCHANNEL */
if (!search_free_spk(1)) { // FIXME: maybe lookup database for extended frequency band before calling subscriber
PDEBUG(DCNETZ, DEBUG_NOTICE, "Outgoing call, but no free channel, rejecting!\n");
return -CAUSE_NOCHANNEL;
}
/* 5. check if we have no OgK, return NOCHANNEL */
/* 4. check if we have no OgK, return NOCHANNEL */
cnetz = search_ogk();
if (!cnetz) {
PDEBUG(DCNETZ, DEBUG_NOTICE, "Outgoing call, but OgK is currently busy, rejecting!\n");
return -CAUSE_NOCHANNEL;
}
/* 5. check if all senders are busy, return NOCHANNEL */
spk = search_free_spk(extended);
if (!spk) {
if (!cnetz->warteschlange) {
PDEBUG(DCNETZ, DEBUG_NOTICE, "Outgoing call, but no free channel, rejecting!\n");
return -CAUSE_NOCHANNEL;
} else
PDEBUG(DCNETZ, DEBUG_NOTICE, "Outgoing call, but no free channel, queuing call!\n");
}
PDEBUG_CHAN(DCNETZ, DEBUG_INFO, "Call to mobile station, paging station id '%s'\n", dialing);
/* 6. trying to page mobile station */
trans = create_transaction(cnetz, TRANS_VAK, dialing[0] - '0', dialing[1] - '0', atoi(dialing + 2), -1);
trans = create_transaction(cnetz, (spk) ? TRANS_VAK : TRANS_WSK, dialing[0] - '0', dialing[1] - '0', atoi(dialing + 2), -1);
if (!trans) {
PDEBUG(DCNETZ, DEBUG_ERROR, "Failed to create transaction\n");
return -CAUSE_TEMPFAIL;
@ -632,8 +666,13 @@ void call_out_disconnect(int callref, int cause)
default:
PDEBUG(DCNETZ, DEBUG_INFO, "Call control disconnects on organisation channel, removing transaction.\n");
call_in_release(callref, cause);
destroy_transaction(trans);
cnetz_go_idle(cnetz);
trans->callref = 0;
if (trans->state == TRANS_MT_QUEUE || trans->state == TRANS_MT_DELAY) {
cnetz_release(trans, cnetz_cause_isdn2cnetz(cause));
} else {
destroy_transaction(trans);
cnetz_go_idle(cnetz);
}
}
}
@ -670,8 +709,12 @@ void call_out_release(int callref, int cause)
break;
default:
PDEBUG(DCNETZ, DEBUG_INFO, "Call control releases on organisation channel, removing transaction.\n");
destroy_transaction(trans);
cnetz_go_idle(cnetz);
if (trans->state == TRANS_MT_QUEUE) {
cnetz_release(trans, cnetz_cause_isdn2cnetz(cause));
} else {
destroy_transaction(trans);
cnetz_go_idle(cnetz);
}
}
}
@ -767,6 +810,22 @@ void transaction_timeout(struct timer *timer)
trans->try++;
trans_new_state(trans, TRANS_VWG);
break;
case TRANS_MT_QUEUE:
PDEBUG_CHAN(DCNETZ, DEBUG_NOTICE, "Phone in queue, but still no channel available, releasing call!\n");
call_in_release(trans->callref, CAUSE_NOCHANNEL);
trans->callref = 0;
cnetz_release(trans, CNETZ_CAUSE_GASSENBESETZT);
break;
case TRANS_MO_QUEUE:
PDEBUG_CHAN(DCNETZ, DEBUG_NOTICE, "Phone in queue, but still no channel available, releasing!\n");
cnetz_release(trans, CNETZ_CAUSE_GASSENBESETZT);
break;
case TRANS_MT_DELAY:
trans_new_state(trans, TRANS_VAK);
break;
case TRANS_MO_DELAY:
trans_new_state(trans, TRANS_VAG);
break;
case TRANS_BQ:
PDEBUG_CHAN(DCNETZ, DEBUG_NOTICE, "No response after channel allocation 'Belegung Quittung'\n");
trans_new_state(trans, TRANS_AF);
@ -808,7 +867,7 @@ void transaction_timeout(struct timer *timer)
destroy_transaction(trans);
break;
default:
PDEBUG_CHAN(DCNETZ, DEBUG_ERROR, "Timeout unhandled in state %d\n", trans->state);
PDEBUG_CHAN(DCNETZ, DEBUG_ERROR, "Timeout unhandled in state %" PRIu64 "\n", trans->state);
}
}
@ -869,13 +928,13 @@ const telegramm_t *cnetz_transmit_telegramm_rufblock(cnetz_t *cnetz)
telegramm.zeitschlitz_nr = cnetz->sched_ts;
telegramm.grenzwert_fuer_einbuchen_und_umbuchen = si[cnetz->cell_nr].grenz_einbuchen;
telegramm.authentifikationsbit = cnetz->auth;
telegramm.vermittlungstechnische_sperren = si[cnetz->cell_nr].sperre;
telegramm.ws_kennung = 0;
telegramm.vermittlungstechnische_sperren = si[cnetz->cell_nr].vermittlungstechnische_sperren;
telegramm.ws_kennung = si[cnetz->cell_nr].ws_kennung;
telegramm.reduzierungsfaktor = si[cnetz->cell_nr].reduzierung;
telegramm.fuz_nationalitaet = si[cnetz->cell_nr].fuz_nat;
telegramm.fuz_fuvst_nr = si[cnetz->cell_nr].fuz_fuvst;
telegramm.fuz_rest_nr = si[cnetz->cell_nr].fuz_rest;
telegramm.kennung_fufst = si[cnetz->cell_nr].fufst_prio;
telegramm.kennung_fufst = si[cnetz->cell_nr].kennung_fufst;
telegramm.nachbarschafts_prioritaets_bit = si[cnetz->cell_nr].nachbar_prio;
telegramm.bewertung_nach_pegel_und_entfernung = si[cnetz->cell_nr].bewertung;
telegramm.entfernungsangabe_der_fufst = si[cnetz->cell_nr].entfernung;
@ -884,11 +943,12 @@ const telegramm_t *cnetz_transmit_telegramm_rufblock(cnetz_t *cnetz)
telegramm.grenzwert_fuer_umschalten = si[cnetz->cell_nr].grenz_umschalten;
telegramm.grenze_fuer_ausloesen = si[cnetz->cell_nr].grenz_ausloesen;
trans = search_transaction(cnetz, TRANS_EM | TRANS_UM | TRANS_WBN | TRANS_WBP | TRANS_VAG | TRANS_VAK);
trans = search_transaction(cnetz, TRANS_EM | TRANS_UM | TRANS_WBN | TRANS_WBP | TRANS_VAG | TRANS_VAK | TRANS_ATQ | TRANS_VA | TRANS_WSK);
if (trans) {
telegramm.futln_nationalitaet = trans->futln_nat;
telegramm.futln_heimat_fuvst_nr = trans->futln_fuvst;
telegramm.futln_rest_nr = trans->futln_rest;
telegramm.ausloesegrund = trans->release_cause;
switch (trans->state) {
case TRANS_EM:
PDEBUG_CHAN(DCNETZ, DEBUG_INFO, "Sending acknowledgment 'Einbuchquittung' to Attachment request.\n");
@ -909,16 +969,26 @@ wbn:
break;
case TRANS_WBP:
spk = search_free_spk(trans->extended);
if (!spk) {
/* Accept call if channel available, otherwise reject or queue call */
if (spk) {
PDEBUG_CHAN(DCNETZ, DEBUG_INFO, "Sending call accept 'Wahlbestaetigung positiv'.\n");
telegramm.opcode = OPCODE_WBP_R;
trans_new_state(trans, TRANS_VAG);
} else if (cnetz->warteschlange) {
/* queue call if no channel is available, but queue allowed */
PDEBUG_CHAN(DCNETZ, DEBUG_INFO, "No free channel, sending call accept in queue 'Wahlbestaetigung positiv in Warteschlage'.\n");
telegramm.opcode = OPCODE_WWBP_R;
trans_new_state(trans, TRANS_MO_QUEUE);
timer_start(&trans->timer, T_VAG2); /* Maximum time to hold queue */
} else {
PDEBUG(DCNETZ, DEBUG_NOTICE, "No free channel anymore, rejecting call!\n");
trans_new_state(trans, TRANS_WBN);
goto wbn;
}
PDEBUG_CHAN(DCNETZ, DEBUG_INFO, "Sending call accept 'Wahlbestaetigung positiv'.\n");
telegramm.opcode = OPCODE_WBP_R;
trans_new_state(trans, TRANS_VAG);
break;
case TRANS_VAG:
case TRANS_VAK:
vak:
if (trans->state == TRANS_VAG) {
PDEBUG_CHAN(DCNETZ, DEBUG_INFO, "Sending channel assignment 'Verbindungsaufbau gehend'.\n");
telegramm.opcode = OPCODE_VAG_R;
@ -956,6 +1026,28 @@ wbn:
/* flush all other transactions, if any (in case of OgK/SpK) */
cnetz_flush_other_transactions(spk, trans);
break;
case TRANS_ATQ:
PDEBUG_CHAN(DCNETZ, DEBUG_INFO, "Sending acknowledgment 'Quittung fuer Ausloesen des FuTelG im OgK-Betrieb' to release request.\n");
telegramm.opcode = OPCODE_ATQ_R;
destroy_transaction(trans);
break;
case TRANS_VA:
PDEBUG_CHAN(DCNETZ, DEBUG_INFO, "Sending 'Vorzeitiges Ausloesen' to queued mobile station\n");
telegramm.opcode = OPCODE_VA_R;
destroy_transaction(trans);
break;
case TRANS_WSK:
spk = search_free_spk(trans->extended);
/* if channel becomes free before we send the queue information, we proceed with channel assignment */
if (spk) {
trans_new_state(trans, TRANS_VAK);
goto vak;
}
PDEBUG_CHAN(DCNETZ, DEBUG_INFO, "No free channel, sending incoming call in queue 'Warteschglange kommend'.\n");
telegramm.opcode = OPCODE_WSK_R;
trans_new_state(trans, TRANS_MT_QUEUE);
timer_start(&trans->timer, T_VAK); /* Maximum time to hold queue */
call_in_alerting(trans->callref);
default:
; /* LR */
}
@ -974,8 +1066,8 @@ const telegramm_t *cnetz_transmit_telegramm_meldeblock(cnetz_t *cnetz)
telegramm.opcode = OPCODE_MLR_M;
telegramm.max_sendeleistung = cnetz->ms_power;
telegramm.ogk_verkehrsanteil = 0; /* must be 0 or phone might not respond to messages in different slot */
telegramm.teilnehmersperre = 0;
telegramm.anzahl_gesperrter_teilnehmergruppen = 0;
telegramm.teilnehmergruppensperre = si[cnetz->cell_nr].teilnehmergruppensperre;
telegramm.anzahl_gesperrter_teilnehmergruppen = si[cnetz->cell_nr].anzahl_gesperrter_teilnehmergruppen;
telegramm.ogk_vorschlag = CNETZ_OGK_KANAL;
telegramm.fuz_rest_nr = si[cnetz->cell_nr].fuz_rest;
@ -1014,7 +1106,6 @@ 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:
@ -1059,6 +1150,13 @@ void cnetz_receive_telegramm_ogk(cnetz_t *cnetz, telegramm_t *telegramm, int blo
}
valid_frame = 1;
break;
case OPCODE_UWG_R:
case OPCODE_UWK_R:
if (!match_fuz(cnetz, telegramm, cnetz->cell_nr))
break;
rufnummer = telegramm2rufnummer(telegramm);
PDEBUG_CHAN(DCNETZ, DEBUG_INFO, "Received Roaming request 'Umbuchantrag' message from Subscriber '%s' on queue\n", rufnummer);
break;
case OPCODE_VWG_R:
case OPCODE_SRG_R:
if (!match_fuz(cnetz, telegramm, cnetz->cell_nr))
@ -1075,16 +1173,10 @@ void cnetz_receive_telegramm_ogk(cnetz_t *cnetz, telegramm_t *telegramm, int blo
break;
}
trans->try = 1;
spk = search_free_spk(trans->extended);
if (!spk) {
PDEBUG(DCNETZ, DEBUG_NOTICE, "Rejecting call from subscriber '%s', because we have no free channel. (or not supported by phone)\n", rufnummer);
trans_new_state(trans, TRANS_WBN);
break;
}
valid_frame = 1;
break;
case OPCODE_WUE_M:
trans = search_transaction(cnetz, TRANS_WAF | TRANS_WBP | TRANS_VAG);
trans = search_transaction(cnetz, TRANS_WAF | TRANS_WBP | TRANS_VAG | TRANS_MO_QUEUE);
if (!trans) {
PDEBUG_CHAN(DCNETZ, DEBUG_NOTICE, "Received dialing digits 'Wahluebertragung' message without transaction, ignoring!\n");
break;
@ -1097,7 +1189,28 @@ void cnetz_receive_telegramm_ogk(cnetz_t *cnetz, telegramm_t *telegramm, int blo
trans->try = 1; /* try */
valid_frame = 1;
break;
case OPCODE_ATO_R:
if (!match_fuz(cnetz, telegramm, cnetz->cell_nr))
break;
rufnummer = telegramm2rufnummer(telegramm);
PDEBUG_CHAN(DCNETZ, DEBUG_INFO, "Received release 'Ausloesen des FuTelG im OgK-Betrieb bei WS' message from Subscriber '%s'\n", rufnummer);
trans = search_transaction_number(cnetz, telegramm->futln_nationalitaet, telegramm->futln_heimat_fuvst_nr, telegramm->futln_rest_nr);
if (!trans) {
/* create transaction, in case the phone repeats the release after we have acked it */
trans = create_transaction(cnetz, TRANS_ATQ, telegramm->futln_nationalitaet, telegramm->futln_heimat_fuvst_nr, telegramm->futln_rest_nr, telegramm->erweitertes_frequenzbandbit);
if (!trans) {
PDEBUG(DCNETZ, DEBUG_ERROR, "Failed to create transaction\n");
break;
}
} else {
timer_stop(&trans->timer);
trans_new_state(trans, TRANS_ATQ);
}
valid_frame = 1;
break;
case OPCODE_MFT_M:
if (!match_fuz(cnetz, telegramm, cnetz->cell_nr))
break;
trans = search_transaction_number(cnetz, telegramm->futln_nationalitaet, telegramm->futln_heimat_fuvst_nr, telegramm->futln_rest_nr);
if (!trans) {
PDEBUG_CHAN(DCNETZ, DEBUG_NOTICE, "Received acknowledge 'Meldung Funktelefonteilnehmer' message without transaction, ignoring!\n");
@ -1392,15 +1505,23 @@ const telegramm_t *cnetz_transmit_telegramm_spk_v(cnetz_t *cnetz)
{
static telegramm_t telegramm;
transaction_t *trans = cnetz->trans_list;
int meter = 0;
memset(&telegramm, 0, sizeof(telegramm));
if (!trans)
return &telegramm;
if (cnetz->metering) {
double now = get_time();
if (!trans->call_start)
trans->call_start = now;
meter = (now - trans->call_start) / (double)cnetz->metering + 1;
}
telegramm.max_sendeleistung = cnetz->ms_power;
telegramm.sendeleistungsanpassung = 1;
telegramm.ankuendigung_gespraechsende = 0;
telegramm.gebuehren_stand = 0;
telegramm.gebuehren_stand = meter;
telegramm.fuz_nationalitaet = si[cnetz->cell_nr].fuz_nat;
telegramm.fuz_fuvst_nr = si[cnetz->cell_nr].fuz_fuvst;
telegramm.fuz_rest_nr = si[cnetz->cell_nr].fuz_rest;
@ -1456,6 +1577,8 @@ void cnetz_receive_telegramm_spk_v(cnetz_t *cnetz, telegramm_t *telegramm)
switch (opcode) {
case OPCODE_VH_V:
case OPCODE_USAI_V:
case OPCODE_USAE_V:
if (!match_fuz(cnetz, telegramm, cnetz->cell_nr)) {
break;
}
@ -1465,7 +1588,17 @@ void cnetz_receive_telegramm_spk_v(cnetz_t *cnetz, telegramm_t *telegramm)
if (trans->state != TRANS_VHQ)
break;
timer_start(&trans->timer, 0.6 * F_VHQ); /* F_VHQ frames */
PDEBUG_CHAN(DCNETZ, DEBUG_INFO, "Received supervisory frame 'Verbindung halten' message.\n");
switch (opcode) {
case OPCODE_VH_V:
PDEBUG_CHAN(DCNETZ, DEBUG_INFO, "Received supervisory frame 'Verbindung halten' message%s.\n", (telegramm->test_telefonteilnehmer_geraet) ? ", phone is a test-phone" : "");
break;
case OPCODE_USAI_V:
PDEBUG_CHAN(DCNETZ, DEBUG_INFO, "Received internal handover request frame 'Umschaltantrag intern' message%s.\n", (telegramm->test_telefonteilnehmer_geraet) ? ", phone is a test-phone" : "");
break;
case OPCODE_USAE_V:
PDEBUG_CHAN(DCNETZ, DEBUG_INFO, "Received external handover request frame 'Umschaltantrag extern' message%s.\n", (telegramm->test_telefonteilnehmer_geraet) ? ", phone is a test-phone" : "");
break;
}
valid_frame = 1;
cnetz->scrambler = telegramm->betriebs_art;
break;

View File

@ -37,6 +37,8 @@ enum cnetz_state {
#define N_AFKT 6 /* number of release frames to send during concentrated signaling */
#define N_AFV 4 /* number of release frames to send during distributed signaling */
#define N 3 /* now many times we repeat a message on OgK */
#define T_VAG2 180 /* time on outgoing queue */
#define T_VAK 60 /* time on incoming queue */
/* clear causes */
#define CNETZ_CAUSE_GASSENBESETZT 0 /* network congested */
@ -71,6 +73,8 @@ typedef struct cnetz {
/* cell config */
int ms_power; /* power level of MS, use 0..3 */
int auth; /* authentication support of the cell */
int warteschlange; /* use queue */
int metering; /* use metering pulses in seconds 0 = off */
/* all cnetz states */
enum cnetz_state state; /* main state of sender */
@ -127,7 +131,7 @@ 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(int kanal, enum cnetz_chan_type chan_type, const char *audiodev, int use_sdr, enum demod_type demod, int samplerate, double rx_gain, int auth, int ms_power, int measure_speed, double clock_speed[2], int polarity, int pre_emphasis, int de_emphasis, const char *write_rx_wave, const char *write_tx_wave, const char *read_rx_wave, const char *read_tx_wave, int loopback);
int cnetz_create(int kanal, enum cnetz_chan_type chan_type, const char *audiodev, int use_sdr, enum demod_type demod, int samplerate, double rx_gain, int auth, int warteschlange, int metering, int ms_power, int measure_speed, double clock_speed[2], int polarity, int pre_emphasis, int de_emphasis, const char *write_rx_wave, const char *write_tx_wave, const char *read_rx_wave, const char *read_tx_wave, int loopback);
void cnetz_destroy(sender_t *sender);
void cnetz_go_idle(cnetz_t *cnetz);
void cnetz_sync_frame(cnetz_t *cnetz, double sync, int ts);

View File

@ -36,6 +36,7 @@
#define MELDE_MAXIMAL 3
typedef struct cnetz_database {
struct cnetz_database *next;
uint8_t futln_nat; /* who ... */
uint8_t futln_fuvst;
@ -154,10 +155,10 @@ int find_db(uint8_t futln_nat, uint8_t futln_fuvst, uint16_t futln_rest)
if (db->futln_nat == futln_nat
&& db->futln_fuvst == futln_fuvst
&& db->futln_rest == futln_rest)
return 1;
return db->extended;
db = db->next;
}
return 0;
return -1;
}
void flush_db(void)

View File

@ -47,7 +47,27 @@ int set_clock_speed = 0;
const char *flip_polarity = "auto";
int ms_power = 0; /* 0..3 */
int auth = 0;
int warteschlange = 1;
uint8_t fuz_nat = 1;
uint8_t fuz_fuvst = 1;
uint8_t fuz_rest = 38;
uint8_t kennung_fufst = 1; /* normal prio */
uint8_t ws_kennung = 0; /* no queue */
uint8_t fuvst_sperren = 0; /* no blocking registration/calls */
uint8_t grenz_einbuchen = 1; /* > 15 SNR */
uint8_t grenz_umschalten = 15; /* < 18 SNR */
uint8_t grenz_ausloesen = 15; /* < 18 SNR */
uint8_t mittel_umschalten = 5; /* 64 Frames */
uint8_t mittel_ausloesen = 5; /* 64 Frames */
uint8_t genauigkeit = 1; /* limited accuracy */
uint8_t bewertung = 1; /* rating by level */
uint8_t entfernung = 3; /* 3km */
uint8_t reduzierung = 0; /* factor 4 */
uint8_t nachbar_prio = 0;
int8_t futln_sperre_start = -1; /* no blocking */
int8_t futln_sperre_end = -1; /* no range */
enum demod_type demod = FSK_DEMOD_AUTO;
int metering = 20;
void print_help(const char *arg0)
{
@ -57,7 +77,7 @@ void print_help(const char *arg0)
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. THIS IS REQUIRED! See documentation!\n");
printf(" -S --clock-speed <rx ppm>,<tx ppm>\n");
printf(" -C --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");
@ -69,6 +89,7 @@ void print_help(const char *arg0)
printf(" base station generates two virtual base stations with both polarities.\n");
printf(" Once a mobile registers, the correct polarity is selected and used.\n");
printf(" (default = %s)\n", flip_polarity);
printf(" Note: This has no effect with SDR.\n");
printf(" -P --ms-power <power level>\n");
printf(" Give power level of the mobile station 0..3. (default = '%d')\n", ms_power);
printf(" 0 = 50-125 mW; 1 = 0.5-1 W; 2 = 4-8 W; 3 = 10-20 W\n");
@ -76,6 +97,90 @@ void print_help(const char *arg0)
printf(" Enable authentication on the base station. Since we cannot\n");
printf(" authenticate, because we don't know the secret key and the algorithm,\n");
printf(" we just accept any card. With this we get the vendor IDs of the phone.\n");
printf(" -Q --queue | --warteschlange 1 | 0\n");
printf(" Enable queue support. If no channel is available, calls will be kept\n");
printf(" in a queue for maximum of 60 seconds. (default = %d)\n", warteschlange);
printf(" -G --gebuehren <seconds> | 0\n");
printf(" Increment metering counter every given number of seconds.\n");
printf(" To turn off, use 0. (default = %d)\n", metering);
printf(" -S --sysinfo fuz-nat=<nat>\n");
printf(" Set country ID of base station. All IDs were used inside Germany only.\n");
printf(" (default = %d)\n", fuz_nat);
printf(" -S --sysinfo fuz-fuvst=<id>\n");
printf(" Set switching center ID of base station. (default = %d)\n", fuz_fuvst);
printf(" -S --sysinfo fuz-rest=<id>\n");
printf(" Set cell ID of base station. (default = %d)\n", fuz_rest);
printf(" -S --sysinfo kennung-fufst=<id>\n");
printf(" Set priority for selecting base station. (default = %d)\n", kennung_fufst);
printf(" 0 = Test (Only special mobile stations may register.)\n");
printf(" 1 = Normal priority base station.\n");
printf(" 2 = Higher priority base station.\n");
printf(" 3 = Highest priority base station.\n");
printf(" Note: Priority has no effect, because there is only one base station.\n");
printf(" -S --sysinfo ws-kennung=<value>\n");
printf(" Queue setting of base station. (default = %d)\n", ws_kennung);
printf(" 0 = No queue, calls will be handled directly.\n");
printf(" 1 = Queue on outgoing calls.\n");
printf(" 2 = Queue blocked, no calls allowed.\n");
printf(" 3 = Reserved, don't use!\n");
printf(" -S --sysinfo fuvst-sperren=<value>\n");
printf(" Blocking registration and outgoing calls. (default = %d)\n", fuvst_sperren);
printf(" 0 = Registration and outgoing calls allowed.\n");
printf(" 1 = Only registration alloweed.\n");
printf(" 2 = Only outgoing calls allowed. (Cannot work without registration!)\n");
printf(" 3 = No registration and no outgoing calls allowed.\n");
printf(" -S --sysinfo grenz-einbuchen=<value>\n");
printf(" Minimum SNR to allow registration of mobile (default = %d)\n", grenz_einbuchen);
printf(" 0 = No limit; 1 = >15 dB; 2 = >17 dB; 3 = >19 dB\n");
printf(" 4 = >21 dB; 5 = >25 dB; 6 = >28 dB; 7 = >32 dB\n");
printf(" -S --sysinfo grenz-umschalten=<value>\n");
printf(" Minimum SNR before phone requests handover (default = %d)\n", grenz_umschalten);
printf(" 15 = 18 dB ... 0 = 26 dB (external)\n");
printf(" 13 = 16 dB ... 0 = 22 dB (internal)\n");
printf(" -S --sysinfo grenz-ausloesen=<value>\n");
printf(" Minimum SNR before phone releases of call (default = %d)\n", grenz_ausloesen);
printf(" 15 = 18 dB ... 0 = 26 dB\n");
printf(" -S --sysinfo mittel-umschalten=<value>\n");
printf(" Number of frames to measure for handover criterium (default = %d)\n", mittel_umschalten);
printf(" 0 = 2 measurements; 1 = 4 measurements; 2 = 8 measurememnts\n");
printf(" 3 = 16 measurements; 4 = 32 measurements; 5 = 64 measurememnts\n");
printf(" -S --sysinfo mittel-ausloesen=<value>\n");
printf(" Number of frames to measure for release criterium (default = %d)\n", mittel_ausloesen);
printf(" 0 = 2 measurements; 1 = 4 measurements; 2 = 8 measurememnts\n");
printf(" 3 = 16 measurements; 4 = 32 measurements; 5 = 64 measurememnts\n");
printf(" -S --sysinfo genauigkeit=<value>\n");
printf(" Accuracy of base station (default = %d)\n", genauigkeit);
printf(" 0 = full accuracy; 1 = limited accuracy\n");
printf(" Note: This has no effect, because there is only one base station.\n");
printf(" -S --sysinfo bewertung=<value>\n");
printf(" Rating of base station (default = %d)\n", bewertung);
printf(" 0 = by relative distance; 1 = by received level\n");
printf(" Note: This has no effect, because there is only one base station.\n");
printf(" -S --sysinfo entfernung=<value>\n");
printf(" Base station size (default = %d)\n", entfernung);
printf(" 0 = 1.5km; 1 = 2km; 2 = 2.5km; 3 = 3km; 4 = 5km; 5 = 5km\n");
printf(" 6 = 6km; 7 = 7km; 8 = 8km; 9 = 10km; 10 = 12km; 11 = 14km\n");
printf(" 12 = 16km; 13 = 17km; 14 = 23km; 15 = 30km\n");
printf(" Note: This has no effect, because there is only one base station.\n");
printf(" -S --sysinfo reduzierung=<value>\n");
printf(" See specs value 'y' (default = %d)\n", reduzierung);
printf(" 0 = 4; 1 = 3; 2 = 2; 3 = 1\n");
printf(" Note: This has no effect, because there is only one base station.\n");
printf(" -S --sysinfo nachbar-prio=<value>\n");
printf(" See specs value 'g' (default = %d)\n", nachbar_prio);
printf(" Note: This has no effect, because there is only one base station.\n");
printf(" -S --sysinfo futln-sperre=<value>[-<value>]\n");
printf(" Blocking registration and outgoing calls for selected mobile stations.\n");
printf(" The 4 least significant bits of the subscriber number can be given to\n");
printf(" block all matching phones. Alternatively the phone number may be given\n");
printf(" here, so that the 4 bits get calculated automatically. The optional\n");
printf(" second value can be given, to define a range - in the same way.\n");
if (futln_sperre_start < 0)
printf(" (default = no value given)\n");
else if (futln_sperre_end < 0)
printf(" (default = %d)\n", futln_sperre_start);
else
printf(" (default = %d-%d)\n", futln_sperre_start, futln_sperre_end);
printf(" -D --demod auto | slope | level\n");
printf(" Adjust demodulation algorithm. Use 'slope' to detect a level change\n");
printf(" by finding the highest slope of a bit transition. It is useful, if\n");
@ -91,6 +196,27 @@ void print_help(const char *arg0)
printf("Press 'i' key to dump list of currently attached subscribers.\n");
}
static int atoi_limit(const char *p, int l1, int l2)
{
int value = atoi(p);
if (l1 < l2) {
if (value < l1)
value = l1;
if (value > l2)
value = l2;
} else {
if (value < l2)
value = l2;
if (value > l1)
value = l1;
}
return value;
}
#define OPT_WARTESCHLANGE 256
static int handle_options(int argc, char **argv)
{
int skip_args = 0;
@ -100,15 +226,19 @@ static int handle_options(int argc, char **argv)
static struct option long_options_special[] = {
{"channel-type", 1, 0, 'T'},
{"measure-speed", 0, 0, 'M'},
{"clock-speed", 1, 0, 'S'},
{"clock-speed", 1, 0, 'C'},
{"flip-polarity", 1, 0, 'F'},
{"ms-power", 1, 0, 'P'},
{"authentication", 0, 0, 'A'},
{"queue", 1, 0, 'Q'},
{"warteschlange", 1, 0, OPT_WARTESCHLANGE},
{"gebuehren", 1, 0, 'G'},
{"sysinfo", 1, 0, 'S'},
{"demod", 1, 0, 'D'},
{0, 0, 0, 0}
};
set_options_common("T:MS:F:N:P:AD:", long_options_special);
set_options_common("T:MC:F:P:AQ:G:S:D:", long_options_special);
while (1) {
int option_index = 0, c;
@ -136,7 +266,7 @@ static int handle_options(int argc, char **argv)
measure_speed = 1;
skip_args++;
break;
case 'S':
case 'C':
p = strchr(optarg, ',');
if (!p) {
fprintf(stderr, "Illegal clock speed, use two values, seperated by comma and no spaces!\n");
@ -161,17 +291,97 @@ static int handle_options(int argc, char **argv)
skip_args += 2;
break;
case 'P':
ms_power = atoi(optarg);
if (ms_power > 3)
ms_power = 3;
if (ms_power < 0)
ms_power = 0;
ms_power = atoi_limit(optarg, 0, 3);
skip_args += 2;
break;
case 'A':
auth = 1;
skip_args += 1;
break;
case 'Q':
case OPT_WARTESCHLANGE:
warteschlange = atoi_limit(optarg, 0, 1);;
skip_args += 2;
break;
case 'G':
metering = atoi(optarg);
skip_args += 2;
break;
case 'S':
p = strchr(optarg, '=');
if (!p) {
fprintf(stderr, "Given sysinfo parameter '%s' requires '=' character to set value, see help!\n", optarg);
exit(0);
}
p++;
if (!strncasecmp(optarg, "fuz-nat=", p - optarg)) {
fuz_nat = atoi_limit(p, 0, 7);
} else
if (!strncasecmp(optarg, "fuz-fuvst=", p - optarg)) {
fuz_fuvst = atoi_limit(p, 0, 32);
} else
if (!strncasecmp(optarg, "fuz-rest=", p - optarg)) {
fuz_rest = atoi_limit(p, 0, 255);
} else
if (!strncasecmp(optarg, "kennung-fufst=", p - optarg)) {
kennung_fufst = atoi_limit(p, 0, 3);
} else
if (!strncasecmp(optarg, "ws-kennung=", p - optarg)) {
ws_kennung = atoi_limit(p, 0, 3);
} else
if (!strncasecmp(optarg, "fuvst-sperren=", p - optarg)) {
fuvst_sperren = atoi_limit(p, 0, 3);
} else
if (!strncasecmp(optarg, "grenz-einbuchen=", p - optarg)) {
grenz_einbuchen = atoi_limit(p, 0, 7);
} else
if (!strncasecmp(optarg, "grenz-umschalten=", p - optarg)) {
grenz_umschalten = atoi_limit(p, 0, 15);
} else
if (!strncasecmp(optarg, "grenz-ausloesen=", p - optarg)) {
grenz_ausloesen = atoi_limit(p, 0, 15);
} else
if (!strncasecmp(optarg, "mittel-umschalten=", p - optarg)) {
mittel_umschalten = atoi_limit(p, 0, 5);
} else
if (!strncasecmp(optarg, "mittel-ausloesen=", p - optarg)) {
mittel_ausloesen = atoi_limit(p, 0, 5);
} else
if (!strncasecmp(optarg, "genauigkeit=", p - optarg)) {
genauigkeit = atoi_limit(p, 0, 1);
} else
if (!strncasecmp(optarg, "bewertung=", p - optarg)) {
bewertung = atoi_limit(p, 0, 1);
} else
if (!strncasecmp(optarg, "entfernung=", p - optarg)) {
entfernung = atoi_limit(p, 0, 15);
} else
if (!strncasecmp(optarg, "nachbar-prio=", p - optarg)) {
nachbar_prio = atoi_limit(p, 0, 1);
} else
if (!strncasecmp(optarg, "futln-sperre=", p - optarg)) {
char value[128], *v, *q;
strncpy(value, p, sizeof(value) - 1);
value[sizeof(value) - 1] = '\0';
v = value;
q = strchr(value, '-');
if (q)
*q++ = '\0';
if (strlen(v) > 5)
v += strlen(v) - 5;
futln_sperre_start = atoi(v) & 0xf;
if (q) {
if (strlen(q) > 5)
q += strlen(q) - 5;
futln_sperre_end = atoi(q) & 0xf;
}
} else
{
fprintf(stderr, "Given sysinfo parameter '%s' unknown, see help!\n", optarg);
exit(0);
}
skip_args += 2;
break;
case 'D':
if (!strcasecmp(optarg, "auto"))
demod = FSK_DEMOD_AUTO;
@ -202,6 +412,8 @@ int main(int argc, char *argv[])
const char *station_id = "";
int mandatory = 0;
int polarity;
int teilnehmergruppensperre = 0;
int anzahl_gesperrter_teilnehmergruppen = 0;
int i;
/* init common tones */
@ -266,7 +478,16 @@ int main(int argc, char *argv[])
/* init functions */
scrambler_init();
init_sysinfo();
if (futln_sperre_start >= 0) {
teilnehmergruppensperre = futln_sperre_start;
if (futln_sperre_end >= 0)
anzahl_gesperrter_teilnehmergruppen = ((futln_sperre_end - futln_sperre_start) & 0xf) + 1;
else
anzahl_gesperrter_teilnehmergruppen = 1;
}
if (anzahl_gesperrter_teilnehmergruppen)
printf("Blocked subscriber with number's last 4 bits from 0x%x to 0x%x\n", teilnehmergruppensperre, (teilnehmergruppensperre + anzahl_gesperrter_teilnehmergruppen - 1) & 0xf);
init_sysinfo(fuz_nat, fuz_fuvst, fuz_rest, kennung_fufst, ws_kennung, fuvst_sperren, grenz_einbuchen, grenz_umschalten, grenz_ausloesen, mittel_umschalten, mittel_ausloesen, genauigkeit, bewertung, entfernung, reduzierung, nachbar_prio, teilnehmergruppensperre, anzahl_gesperrter_teilnehmergruppen);
dsp_init();
rc = init_telegramm();
if (rc < 0) {
@ -336,7 +557,7 @@ int main(int argc, char *argv[])
/* create transceiver instance */
for (i = 0; i < num_kanal; i++) {
rc = cnetz_create(kanal[i], chan_type[i], audiodev[i], use_sdr, demod, samplerate, rx_gain, auth, ms_power, (i == 0) ? measure_speed : 0, clock_speed, polarity, do_pre_emphasis, do_de_emphasis, write_rx_wave, write_tx_wave, read_rx_wave, read_tx_wave, loopback);
rc = cnetz_create(kanal[i], chan_type[i], audiodev[i], use_sdr, demod, samplerate, rx_gain, auth, warteschlange, metering, ms_power, (i == 0) ? measure_speed : 0, clock_speed, polarity, do_pre_emphasis, do_de_emphasis, write_rx_wave, write_tx_wave, read_rx_wave, read_tx_wave, loopback);
if (rc < 0) {
fprintf(stderr, "Failed to create \"Sender\" instance. Quitting!\n");
goto fail;

View File

@ -4,61 +4,57 @@
cnetz_si si[2];
void init_sysinfo(void)
void init_sysinfo(uint8_t fuz_nat, uint8_t fuz_fuvst, uint8_t fuz_rest, uint8_t kennung_fufst, uint8_t ws_kennung, uint8_t vermittlungstechnische_sperren, uint8_t grenz_einbuchen, uint8_t grenz_umschalten, uint8_t grenz_ausloesen, uint8_t mittel_umschalten, uint8_t mittel_ausloesen, uint8_t genauigkeit, uint8_t bewertung, uint8_t entfernung, uint8_t reduzierung, uint8_t nachbar_prio, int8_t teilnehmergruppensperre, uint8_t anzahl_gesperrter_teilnehmergruppen)
{
memset(&si, 0, sizeof(si));
memset(&si[0], 0, sizeof(cnetz_si));
/* polarity of TX signal */
si[0].flip_polarity = 0;
/* ID of base station */
si[0].fuz_nat = fuz_nat;
si[0].fuz_fuvst = fuz_fuvst;
si[0].fuz_rest = fuz_rest;
/* a low value causes quicker measurement results */
si[0].mittel_umschalten = mittel_umschalten; /* 0..5 */
/* a high value is tollerant to bad quality */
si[0].grenz_umschalten = grenz_umschalten; /* 0..15 */
/* a low value causes quicker measurement results */
si[0].mittel_ausloesen = mittel_ausloesen; /* 0..5 */
/* a high value is tollerant to bad quality */
si[0].grenz_ausloesen = grenz_ausloesen; /* 0..15 */
si[0].vermittlungstechnische_sperren = vermittlungstechnische_sperren;
si[0].genauigkeit = genauigkeit; /* 1 = bedingte Genauigkeit */
si[0].entfernung = entfernung;
/* a low value is tollerant to bad quality */
si[0].grenz_einbuchen = grenz_einbuchen; /* 1..7 */
si[0].kennung_fufst = kennung_fufst;
si[0].ws_kennung = ws_kennung;
si[0].nachbar_prio = nachbar_prio;
si[0].bewertung = bewertung; /* 0 = relative entfernung, 1 = pegel */
si[0].reduzierung = reduzierung;
/* deny group of subscribers. (used to balance subscribers between base stations) */
si[0].teilnehmergruppensperre = teilnehmergruppensperre;
si[0].anzahl_gesperrter_teilnehmergruppen = anzahl_gesperrter_teilnehmergruppen;
/* second cell uses flipped polarity. different station ID is used to
* detect what cell (and what polarity) the mobile responses to. */
memcpy(&si[1], &si[0], sizeof(cnetz_si));
si[1].flip_polarity = 1;
si[0].fuz_nat = 1;
si[1].fuz_nat = 1;
si[0].fuz_fuvst = 1;
si[1].fuz_fuvst = 1;
si[0].fuz_rest = 38;
si[1].fuz_rest = 39;
/* a low value causes quicker measurement results */
si[0].mittel_umschalten = 5; /* 0..5 */
si[1].mittel_umschalten = 5;
/* a low value is tollerant to bad quality */
si[0].grenz_umschalten = 0; /* 0..13 */
si[1].grenz_umschalten = 0; /* 0..13 */
/* a low value causes quicker measurement results */
si[0].mittel_ausloesen = 5; /* 0..5 */
si[1].mittel_ausloesen = 5;
/* a low value is tollerant to bad quality */
si[0].grenz_ausloesen = 0; /* 0..13 */
si[1].grenz_ausloesen = 0; /* 0..13 */
si[0].sperre = 0;
si[1].sperre = 0;
si[0].genauigkeit = 1; /* bedingte Genauigkeit */
si[1].genauigkeit = 1; /* bedingte Genauigkeit */
si[0].entfernung = 3;
si[1].entfernung = 3;
/* a low value is tollerant to bad quality */
si[0].grenz_einbuchen = 1; /* 1..7 */
si[1].grenz_einbuchen = 1;
si[0].fufst_prio = 1; /* normal pio */
si[1].fufst_prio = 1; /* normal pio */
si[0].nachbar_prio = 0;
si[1].nachbar_prio = 0;
si[0].bewertung = 1; /* pegel */
si[1].bewertung = 1; /* pegel */
si[0].reduzierung = 0;
si[1].reduzierung = 0;
si[1].fuz_rest = si[0].fuz_rest + 1;
}

View File

@ -8,17 +8,20 @@ typedef struct system_information {
uint8_t grenz_umschalten;
uint8_t mittel_ausloesen;
uint8_t grenz_ausloesen;
uint8_t sperre;
uint8_t vermittlungstechnische_sperren;
uint8_t genauigkeit;
uint8_t entfernung;
uint8_t grenz_einbuchen;
uint8_t fufst_prio; /* prio of base station */
uint8_t kennung_fufst; /* prio of base station */
uint8_t ws_kennung; /* queue setting sof base station */
uint8_t nachbar_prio;
uint8_t bewertung;
uint8_t reduzierung;
int8_t teilnehmergruppensperre;
int8_t anzahl_gesperrter_teilnehmergruppen;
} cnetz_si;
extern cnetz_si si[];
void init_sysinfo(void);
void init_sysinfo(uint8_t fuz_nat, uint8_t fuz_fuvst, uint8_t fuz_rest, uint8_t kennung_fufst, uint8_t ws_kennung, uint8_t vermittlungstechnische_sperren, uint8_t grenz_einbuchen, uint8_t grenz_umschalten, uint8_t grenz_ausloesen, uint8_t mittel_umschalten, uint8_t mittel_ausloesen, uint8_t genauigkeit, uint8_t bewertung, uint8_t entfernung, uint8_t reduzierung, uint8_t nachbar_prio, int8_t teilnehmergruppensperre, uint8_t anzahl_gesperrter_teilnehmergruppen);

View File

@ -114,7 +114,7 @@ static const char *param_genauigkeit[] = {
};
static const char *param_grenzwert[] = {
"Illegaler Parameter 0",
"No limit",
"> 15 dB S/N",
"> 17 dB S/N",
"> 19 dB S/N",
@ -272,7 +272,7 @@ static struct definition_parameter {
{ 'U',"FuTln-Heimmat FuVSt-Nr.", 5, NULL },
{ 'V',"Sicherungs-Code", 16, NULL },
{ 'W',"WS-Kennung", 2, param_wskennung },
{ 'X',"Wahlziffer beliebig 16 Ziffer", 64, NULL },
{ 'X',"Wahlziffer beliebig 16 Ziffern", 64, NULL },
{ 'Z',"Zeitschlitz-Nr.", 5, NULL },
{ 'a',"Grenzert fuer Ausloesen", 4, param_ausloesen },
{ 'b',"Chipkarten-FuTelG-Bit", 1, param_chipkarte },
@ -363,7 +363,7 @@ static struct definition_opcode {
{ "________________________________________________________________", NULL, "opcode 34",BLOCK_I,"Illegaler Opcode" },
{ "PPdZZZZZ----------------IIIAAAAAFFFFFFFFNNNUUUUUTTTTTTTTTTTTTTTT", NULL, "EBQ(R)", BLOCK_R,"Einbuchquittung" },
{ "PPdZZZZZ----------------IIIAAAAAFFFFFFFFNNNUUUUUTTTTTTTTTTTTTTTT", NULL, "UBQ(R)", BLOCK_R,"Umbuchquittung" },
{ "PPdZZZZZ----------------IIIAAAAAFFFFFFFFNNNUUUUUTTTTTTTTTTTTTTTT", NULL, "WSK(R)", BLOCK_R,"Wartescglange kommend" },
{ "PPdZZZZZ----------------IIIAAAAAFFFFFFFFNNNUUUUUTTTTTTTTTTTTTTTT", NULL, "WSK(R)", BLOCK_R,"Warteschglange kommend" },
{ "PP-MMMMMDDDDEEEE------HHHHHHHHHHFFFFFFFF------------------------", NULL, "MLR(M)", BLOCK_M,"Melde-Leer-Ruf" },
{ "PPdZZZZZffflvvWW------yyIIIAAAAAFFFFFFFFkkgprrrrmmmmnnnnuuuuaaaa", NULL, "LR(R)", BLOCK_R,"Leer-Ruf" },
{ "PPdZZZZZ----------------IIIAAAAAFFFFFFFFNNNUUUUUTTTTTTTTTTTTTTTT", NULL, "ATQ(R)", BLOCK_R,"Quittung fuer Ausloesen des FuTelG im OgK-Betrieb" },
@ -655,7 +655,7 @@ static char *assemble_telegramm(const telegramm_t *telegramm, int debug)
value = telegramm->ankuendigung_gespraechsende;
break;
case 'D':
value = telegramm->teilnehmersperre;
value = telegramm->teilnehmergruppensperre;
break;
case 'E':
value = telegramm->anzahl_gesperrter_teilnehmergruppen;
@ -868,7 +868,7 @@ static void disassemble_telegramm(telegramm_t *telegramm, const char *bits, int
telegramm->ankuendigung_gespraechsende = value;
break;
case 'D':
telegramm->teilnehmersperre = value;
telegramm->teilnehmergruppensperre = value;
break;
case 'E':
telegramm->anzahl_gesperrter_teilnehmergruppen = value;
@ -1533,7 +1533,7 @@ selected:
nr = 1;
goto selected;
} else {
PDEBUG(DFRAME, DEBUG_NOTICE, "Received Telegramm with no cell number, ignoring!\n");
PDEBUG(DFRAME, DEBUG_NOTICE, "Received Telegramm with unknown cell number, ignoring!\n");
return;
}
}

View File

@ -67,7 +67,7 @@ typedef struct telegramm {
uint8_t fuz_fuvst_nr;
uint8_t betriebs_art;
uint8_t ankuendigung_gespraechsende;
uint8_t teilnehmersperre;
uint8_t teilnehmergruppensperre;
uint8_t anzahl_gesperrter_teilnehmergruppen;
uint8_t fuz_rest_nr;
uint16_t gebuehren_stand;

View File

@ -39,7 +39,7 @@ const char *transaction2rufnummer(transaction_t *trans)
}
/* create transaction */
transaction_t *create_transaction(cnetz_t *cnetz, uint32_t state, uint8_t futln_nat, uint8_t futln_fuvst, uint16_t futln_rest, int extended)
transaction_t *create_transaction(cnetz_t *cnetz, uint64_t state, uint8_t futln_nat, uint8_t futln_fuvst, uint16_t futln_rest, int extended)
{
sender_t *sender;
transaction_t *trans = NULL;
@ -80,7 +80,7 @@ transaction_t *create_transaction(cnetz_t *cnetz, uint32_t state, uint8_t futln_
if (state == TRANS_VWG)
trans->mo_call = 1;
if (state == TRANS_VAK)
if (state == TRANS_VAK || state == TRANS_WSK)
trans->mt_call = 1;
const char *rufnummer = transaction2rufnummer(trans);
@ -147,7 +147,7 @@ void unlink_transaction(transaction_t *trans)
cnetz_display_status();
}
transaction_t *search_transaction(cnetz_t *cnetz, uint32_t state_mask)
transaction_t *search_transaction(cnetz_t *cnetz, uint64_t state_mask)
{
transaction_t *trans = cnetz->trans_list;
@ -200,7 +200,7 @@ transaction_t *search_transaction_callref(cnetz_t *cnetz, int callref)
return NULL;
}
static const char *trans_state_name(int state)
static const char *trans_state_name(uint64_t state)
{
switch (state) {
case 0:
@ -223,6 +223,8 @@ static const char *trans_state_name(int state)
return "WBN";
case TRANS_VAG:
return "VAG";
case TRANS_WSK:
return "WSK";
case TRANS_VAK:
return "VAK";
case TRANS_BQ:
@ -235,16 +237,28 @@ static const char *trans_state_name(int state)
return "DS";
case TRANS_AHQ:
return "AHQ";
case TRANS_VA:
return "VA";
case TRANS_AF:
return "AF";
case TRANS_AT:
return "AT";
case TRANS_ATQ:
return "ATQ";
case TRANS_MO_QUEUE:
return "MO_QUEUE";
case TRANS_MT_QUEUE:
return "MT_QUEUE";
case TRANS_MO_DELAY:
return "MO_DELAY";
case TRANS_MT_DELAY:
return "MT_DELAY";
default:
return "<invald transaction state>";
}
}
const char *trans_short_state_name(int state)
const char *trans_short_state_name(uint64_t state)
{
switch (state) {
case 0:
@ -279,9 +293,9 @@ const char *trans_short_state_name(int state)
}
}
void trans_new_state(transaction_t *trans, int state)
void trans_new_state(transaction_t *trans, uint64_t state)
{
PDEBUG(DTRANS, DEBUG_INFO, "Transaction state %s -> %s\n", trans_state_name(trans->state), trans_state_name(state));
PDEBUG(DTRANS, DEBUG_INFO, "Transaction (%s) state %s -> %s\n", transaction2rufnummer(trans), trans_state_name(trans->state), trans_state_name(state));
trans->state = state;
cnetz_display_status();
}

View File

@ -13,16 +13,24 @@
#define TRANS_WBN (1 << 7) /* dialing received, waiting for time slot to reject call */
#define TRANS_VAG (1 << 8) /* establishment of call sent, switching channel */
/* mobile terminated call */
#define TRANS_VAK (1 << 9) /* establishment of call sent, switching channel */
#define TRANS_WSK (1 << 9) /* incomming call in queue */
#define TRANS_VAK (1 << 10) /* establishment of call sent, switching channel */
/* traffic channel */
#define TRANS_BQ (1 << 10) /* accnowledge channel */
#define TRANS_VHQ (1 << 11) /* hold call */
#define TRANS_RTA (1 << 12) /* hold call and make the phone ring */
#define TRANS_DS (1 << 13) /* establish speech connection */
#define TRANS_AHQ (1 << 14) /* establish speech connection after answer */
#define TRANS_BQ (1 << 11) /* accnowledge channel */
#define TRANS_VHQ (1 << 12) /* hold call */
#define TRANS_RTA (1 << 13) /* hold call and make the phone ring */
#define TRANS_DS (1 << 14) /* establish speech connection */
#define TRANS_AHQ (1 << 15) /* establish speech connection after answer */
/* release */
#define TRANS_AF (1 << 15) /* release connection by base station */
#define TRANS_AT (1 << 16) /* release connection by mobile station */
#define TRANS_VA (1 << 16) /* release call in queue by base station (OgK) */
#define TRANS_AF (1 << 17) /* release connection by base station (SpK) */
#define TRANS_AT (1 << 18) /* release connection by mobile station */
#define TRANS_ATQ (1 << 19) /* acknowledge release of MO call in queue */
/* queue */
#define TRANS_MO_QUEUE (1 << 20) /* MO queue */
#define TRANS_MT_QUEUE (1 << 21) /* MT queue */
#define TRANS_MO_DELAY (1 << 22) /* delay to be sure the channel is free again */
#define TRANS_MT_DELAY (1 << 23)
typedef struct transaction {
struct transaction *next; /* pointer to next node in list */
@ -33,7 +41,7 @@ typedef struct transaction {
uint16_t futln_rest;
int extended; /* extended frequency capability */
char dialing[17]; /* number dialed by the phone */
int32_t state; /* state of transaction */
int64_t state; /* state of transaction */
int8_t release_cause; /* reason for release, (c-netz coding) */
int try; /* counts resending messages */
int repeat; /* counts repeating messages */
@ -41,18 +49,19 @@ typedef struct transaction {
int mo_call; /* flags a moile originating call */
int mt_call; /* flags a moile terminating call */
int page_failed; /* failed to get a response from MS */
double call_start; /* when did the call start? (used for metering) */
} transaction_t;
const char *transaction2rufnummer(transaction_t *trans);
transaction_t *create_transaction(cnetz_t *cnetz, uint32_t state, uint8_t futln_nat, uint8_t futln_fuvst, uint16_t futln_rest, int extended);
transaction_t *create_transaction(cnetz_t *cnetz, uint64_t state, uint8_t futln_nat, uint8_t futln_fuvst, uint16_t futln_rest, int extended);
void destroy_transaction(transaction_t *trans);
void link_transaction(transaction_t *trans, cnetz_t *cnetz);
void unlink_transaction(transaction_t *trans);
transaction_t *search_transaction(cnetz_t *cnetz, uint32_t state_mask);
transaction_t *search_transaction(cnetz_t *cnetz, uint64_t state_mask);
transaction_t *search_transaction_number(cnetz_t *cnetz, uint8_t futln_nat, uint8_t futln_fuvst, uint16_t futln_rest);
transaction_t *search_transaction_callref(cnetz_t *cnetz, int callref);
void trans_new_state(transaction_t *trans, int state);
void trans_new_state(transaction_t *trans, uint64_t state);
void cnetz_flush_other_transactions(cnetz_t *cnetz, transaction_t *trans);
void transaction_timeout(struct timer *timer);
const char *trans_short_state_name(int state);
const char *trans_short_state_name(uint64_t state);