Compare commits

...

18 Commits

Author SHA1 Message Date
Andreas Eversberg f4aa9b79bb Osmo-CC: Minor compiler warning fixes 2022-06-19 06:51:49 +02:00
Andreas Eversberg 641ba0881f R2000: Show debugging for IDLE frames
Suppress repeated IDLE frame
2022-06-19 06:51:48 +02:00
Andreas Eversberg 181617aa81 NMT: Show debugging for IDLE frames
Suppress repeated IDLE frame
2022-06-19 06:51:45 +02:00
Andreas Eversberg 6f84314b93 AMPS: Show debugging for filler/system frames
Show them only once when FOCC is enabled.
2022-06-19 06:51:43 +02:00
Andreas Eversberg 253dfcc8bb C-Netz: Fix station list and selection 2022-06-19 06:51:40 +02:00
Andreas Eversberg 7cd3f85294 C-Netz: Show debugging for IDLE (LR and LMR) frames
Suppress repeated IDLE frame
2022-06-19 06:51:38 +02:00
Andreas Eversberg ec286f3d94 C-Netz: Add options to change 'Meldeaufruf' timer and counter. 2022-06-19 06:51:35 +02:00
Andreas Eversberg cb9c85adb5 C-Netz: Add capability of special tunnel base station (bahn-bs)
Totally useless, if you don't have a train phone - don't you?
2022-06-19 06:51:33 +02:00
Andreas Eversberg 26f4b3f711 C-Netz: Cleanup OgK slot allocation and polarity detection
Now you can select between 1 and 8 slots per frame.
2022-06-19 06:51:30 +02:00
Andreas Eversberg 4930da8e0c C-Netz: Add capability of non-standard OgK channel(s) 2022-06-19 06:51:28 +02:00
Andreas Eversberg 4758f16324 Implementation of DCF77 transmitter and receiver 2022-06-18 21:26:02 +02:00
Andreas Eversberg 8c382431b5 sdr: RF level is only provided when requested and available 2022-06-18 21:25:59 +02:00
Andreas Eversberg 1708d24d73 Alsa will indivate RF level with 0 dB 2022-06-18 21:25:58 +02:00
Andreas Eversberg df2b017472 debug: Show debug category together with debug level 2022-06-18 21:25:57 +02:00
Andreas Eversberg 98ed54124a Fixed glitch in sound buffer space calculation
Sometimes the sound buffer (ALSA) returns a few more samples than we
sent to it. In this case we return 0, which means that no data has
to be sent.
2022-06-10 20:22:10 +02:00
Andreas Eversberg ba4c095d09 Added UK0 and PH socket to libdebug 2022-05-29 17:07:28 +02:00
Andreas Eversberg fd81881922 ALSA: Add environment variable to delay input
This helps to avoid corrupted date in rx buffer.

Sorry: No more info, because I really don't know why this happens.
2022-05-29 17:06:23 +02:00
Andreas Eversberg e453c10169 Work on docs 2022-05-29 17:06:21 +02:00
37 changed files with 1746 additions and 264 deletions

1
.gitignore vendored
View File

@ -80,6 +80,7 @@ src/sim/cnetz_sim
src/magnetic/cnetz_magnetic
src/fuvst/fuvst
src/fuvst/fuvst_sniffer
src/dcf77/dcf77
extra/cnetz_memory_card_generator
src/test/test_filter
src/test/test_sendevolumenregler

3
README
View File

@ -27,8 +27,7 @@ Additionally the following communication services are implemented:
* Analog Modem Emulation (AM7911)
* German classic 'Zeitansage' (talking clock)
* POCSAG transmitter / receiver
* 5-Ton-Folge + Sirenensteuerung
* DCF77 time signal transmitter and receiver
USE AT YOUR OWN RISK!

View File

@ -114,6 +114,7 @@ AC_OUTPUT(
src/sim/Makefile
src/magnetic/Makefile
src/fuvst/Makefile
src/dcf77/Makefile
src/test/Makefile
src/Makefile
extra/Makefile

View File

@ -15,7 +15,7 @@ To get a list of all options, run the base station software (E.g bnetz) with no
</p>
<p class="toppic">
Sound interface
Sound interface using ALSA
</p>
<p>
@ -64,6 +64,69 @@ If you prefer card 2, device 0, add '-a hw:2,0' or '--audio-device hw:2,0' to th
</pre>
<p class="toppic">
Prevent Puleaudio from occupying a sound adapter
</p>
<p>
In some cases, pulseaudio occupies the sound interface, so that it cannot be used via ALSA API.
"Device or resource busy" will be reported by the application.
One way would be to stop pulseaudio daemon, if there is only a single audio device available.
If an extra sound device, like an USB stick shall be used, we can just block it for pulseaudio, using udev rule.
</p>
<p>
First, we need to check the device ID using lsusb (USB) or lspci (PCI):
</p>
<pre>
# lsusb
...
Bus 001 Device 030: ID 0bda:4937 Realtek Semiconductor Corp. Realtek Audio USB
...
</pre>
<p>
In this case there is an USB stick with vendor ID "0bda" and device ID "4937".
If nano is our favorite editor, we add a new udev rule (sudo or be root):
</p>
<pre>
# nano /etc/udev/rules.d/89-pulseaudio-ignore_0bda_4937.rules
</pre>
<p>
Choose any index (like 89), but be sure that it must be lower than any other pulseaudio rule, if there is any at all. Add some name that explains exactly what device is blocked, but feel free to user any other name.
</p>
<p>
Add this line to block the device shown above, but use the right IDs:
</p>
<pre>
ATTRS{idVendor}=="0bda", ATTRS{idProduct}=="4937", ENV{PULSE_IGNORE}="1"
</pre>
<p>
When using PCI, you need to reboot.
When using USB, unplug it and then load the new rules before replugging it:
</p>
<pre>
udevadm control --reload-rules
</pre>
<p>
To verify that the rule works, check if the device is available using "pavucontrol".
Do that before and after, to see the effect.
</p>
<p class="toppic">
Basic level adjustment
</p>

View File

@ -62,7 +62,8 @@ SUBDIRS += \
zeitansage \
sim \
magnetic \
fuvst
fuvst \
dcf77
if HAVE_ALSA
if HAVE_FUSE

View File

@ -133,6 +133,7 @@ struct amps {
uint8_t tx_focc_order;
int tx_focc_word_count; /* counts transmitted words in a multi word message */
int tx_focc_word_repeat; /* counts repeats of multi word message */
int tx_focc_debugged; /* indicator to prevent debugging all SI/filler frames */
/* FVC frame states */
int tx_fvc_send; /* if set, send message words */
int tx_fvc_chan; /* channel to assign for voice call */

View File

@ -920,6 +920,7 @@ void amps_set_dsp_mode(amps_t *amps, enum dsp_mode mode, int frame_length)
/* reset SAT detection */
sat_reset(amps, "Change to FOCC");
PDEBUG_CHAN(DDSP, DEBUG_INFO, "Change mode to FOCC\n");
amps->tx_focc_debugged = 0;
}
if (amps->dsp_mode == DSP_MODE_FRAME_RX_FRAME_TX
&& (mode == DSP_MODE_AUDIO_RX_AUDIO_TX || mode == DSP_MODE_AUDIO_RX_FRAME_TX)) {

View File

@ -2707,7 +2707,7 @@ static uint64_t amps_encode_word(frame_t *frame, struct def_word *w, int debug)
int i, t4 = 0;
#ifdef DEBUG_ALL_MESSAGES
debug=1;
debug = 1;
#endif
memset(spaces, ' ', ie_desc_max_len);
@ -2718,7 +2718,8 @@ static uint64_t amps_encode_word(frame_t *frame, struct def_word *w, int debug)
for (i = 0; w->ie[i].name; i++)
sum_bits += w->ie[i].bits;
PDEBUG(DFRAME, (debug >= 0) ? DEBUG_INFO : DEBUG_DEBUG, "Transmit: %s\n", w->name);
if (debug)
PDEBUG(DFRAME, DEBUG_INFO, "Transmit: %s\n", w->name);
word = 0;
for (i = 0; w->ie[i].name; i++) {
bits = w->ie[i].bits;
@ -2727,7 +2728,7 @@ static uint64_t amps_encode_word(frame_t *frame, struct def_word *w, int debug)
else
value = frame->ie[w->ie[i].ie];
word = (word << bits) | (value & cut_bits[bits]);
if (debug >= 0) {
if (debug) {
if (amps_ie_desc[w->ie[i].ie].decoder)
PDEBUG(DFRAME, DEBUG_DEBUG, " %s%s: %" PRIu64 " = %s (%s)\n", spaces + strlen(w->ie[i].name), w->ie[i].name, value, amps_ie_desc[w->ie[i].ie].decoder(value), amps_ie_desc[w->ie[i].ie].desc);
else
@ -2738,7 +2739,7 @@ static uint64_t amps_encode_word(frame_t *frame, struct def_word *w, int debug)
t4++;
if (t4 == 3) {
t4 = 0;
if (debug >= 0)
if (debug)
PDEBUG(DFRAME, DEBUG_DEBUG, " %s--> %s\n", spaces, amps_table4_name(frame->ie[AMPS_IE_LOCAL_MSG_TYPE], frame->ie[AMPS_IE_ORDQ], frame->ie[AMPS_IE_ORDER]));
}
}
@ -2746,7 +2747,7 @@ static uint64_t amps_encode_word(frame_t *frame, struct def_word *w, int debug)
return word;
}
static uint64_t amps_encode_control_filler(amps_t *amps, uint8_t dcc, uint8_t cmac, uint8_t sdcc1, uint8_t sdcc2, uint8_t wfom)
static uint64_t amps_encode_control_filler(amps_t *amps, uint8_t dcc, uint8_t cmac, uint8_t sdcc1, uint8_t sdcc2, uint8_t wfom, int debug)
{
frame_t frame;
@ -2767,10 +2768,10 @@ static uint64_t amps_encode_control_filler(amps_t *amps, uint8_t dcc, uint8_t cm
} else
frame.ie[AMPS_IE_1111] = 15;
frame.ie[AMPS_IE_OHD] = 1;
return amps_encode_word(&frame, &control_filler, -1);
return amps_encode_word(&frame, &control_filler, debug);
}
uint64_t amps_encode_word1_system(uint8_t dcc, uint16_t sid1, uint8_t ep, uint8_t auth, uint8_t pci, uint8_t nawc)
uint64_t amps_encode_word1_system(uint8_t dcc, uint16_t sid1, uint8_t ep, uint8_t auth, uint8_t pci, uint8_t nawc, int debug)
{
frame_t frame;
@ -2783,10 +2784,10 @@ uint64_t amps_encode_word1_system(uint8_t dcc, uint16_t sid1, uint8_t ep, uint8_
frame.ie[AMPS_IE_PCI] = pci;
frame.ie[AMPS_IE_NAWC] = nawc;
frame.ie[AMPS_IE_OHD] = 6;
return amps_encode_word(&frame, &amps_word1_system_parameter_overhead, -1);
return amps_encode_word(&frame, &amps_word1_system_parameter_overhead, debug);
}
uint64_t tacs_encode_word1_system(uint8_t dcc, uint16_t aid1, uint8_t ep, uint8_t auth, uint8_t pci, uint8_t nawc)
uint64_t tacs_encode_word1_system(uint8_t dcc, uint16_t aid1, uint8_t ep, uint8_t auth, uint8_t pci, uint8_t nawc, int debug)
{
frame_t frame;
@ -2799,10 +2800,10 @@ uint64_t tacs_encode_word1_system(uint8_t dcc, uint16_t aid1, uint8_t ep, uint8_
frame.ie[AMPS_IE_PCI] = pci;
frame.ie[AMPS_IE_NAWC] = nawc;
frame.ie[AMPS_IE_OHD] = 6;
return amps_encode_word(&frame, &tacs_word1_system_parameter_overhead, -1);
return amps_encode_word(&frame, &tacs_word1_system_parameter_overhead, debug);
}
uint64_t amps_encode_word2_system(uint8_t dcc, uint8_t s, uint8_t e, uint8_t regh, uint8_t regr, uint8_t dtx, uint8_t n_1, uint8_t rcf, uint8_t cpa, uint8_t cmax_1, uint8_t end)
uint64_t amps_encode_word2_system(uint8_t dcc, uint8_t s, uint8_t e, uint8_t regh, uint8_t regr, uint8_t dtx, uint8_t n_1, uint8_t rcf, uint8_t cpa, uint8_t cmax_1, uint8_t end, int debug)
{
frame_t frame;
@ -2820,10 +2821,10 @@ uint64_t amps_encode_word2_system(uint8_t dcc, uint8_t s, uint8_t e, uint8_t reg
frame.ie[AMPS_IE_CMAX_1] = cmax_1;
frame.ie[AMPS_IE_END] = end;
frame.ie[AMPS_IE_OHD] = 7;
return amps_encode_word(&frame, &word2_system_parameter_overhead, -1);
return amps_encode_word(&frame, &word2_system_parameter_overhead, debug);
}
uint64_t amps_encode_registration_id(uint8_t dcc, uint32_t regid, uint8_t end)
uint64_t amps_encode_registration_id(uint8_t dcc, uint32_t regid, uint8_t end, int debug)
{
frame_t frame;
@ -2833,10 +2834,10 @@ uint64_t amps_encode_registration_id(uint8_t dcc, uint32_t regid, uint8_t end)
frame.ie[AMPS_IE_REGID] = regid;
frame.ie[AMPS_IE_END] = end;
frame.ie[AMPS_IE_OHD] = 0;
return amps_encode_word(&frame, &registration_id, -1);
return amps_encode_word(&frame, &registration_id, debug);
}
uint64_t amps_encode_registration_increment(uint8_t dcc, uint16_t regincr, uint8_t end)
uint64_t amps_encode_registration_increment(uint8_t dcc, uint16_t regincr, uint8_t end, int debug)
{
frame_t frame;
@ -2847,10 +2848,10 @@ uint64_t amps_encode_registration_increment(uint8_t dcc, uint16_t regincr, uint8
frame.ie[AMPS_IE_REGINCR] = regincr;
frame.ie[AMPS_IE_END] = end;
frame.ie[AMPS_IE_OHD] = 4;
return amps_encode_word(&frame, &registration_increment_global_action, -1);
return amps_encode_word(&frame, &registration_increment_global_action, debug);
}
uint64_t amps_encode_location_area(uint8_t dcc, uint8_t pureg, uint8_t pdreg, uint8_t lreg, uint16_t locaid, uint8_t end)
uint64_t amps_encode_location_area(uint8_t dcc, uint8_t pureg, uint8_t pdreg, uint8_t lreg, uint16_t locaid, uint8_t end, int debug)
{
frame_t frame;
@ -2864,10 +2865,10 @@ uint64_t amps_encode_location_area(uint8_t dcc, uint8_t pureg, uint8_t pdreg, ui
frame.ie[AMPS_IE_LOCAID] = locaid;
frame.ie[AMPS_IE_END] = end;
frame.ie[AMPS_IE_OHD] = 4;
return amps_encode_word(&frame, &location_area_global_action, -1);
return amps_encode_word(&frame, &location_area_global_action, debug);
}
uint64_t amps_encode_new_access_channel_set(uint8_t dcc, uint16_t newacc, uint8_t end)
uint64_t amps_encode_new_access_channel_set(uint8_t dcc, uint16_t newacc, uint8_t end, int debug)
{
frame_t frame;
@ -2878,10 +2879,10 @@ uint64_t amps_encode_new_access_channel_set(uint8_t dcc, uint16_t newacc, uint8_
frame.ie[AMPS_IE_NEWACC] = newacc;
frame.ie[AMPS_IE_END] = end;
frame.ie[AMPS_IE_OHD] = 4;
return amps_encode_word(&frame, &new_access_channel_set_global_action, -1);
return amps_encode_word(&frame, &new_access_channel_set_global_action, debug);
}
uint64_t amps_encode_overload_control(uint8_t dcc, uint8_t *olc, uint8_t end)
uint64_t amps_encode_overload_control(uint8_t dcc, uint8_t *olc, uint8_t end, int debug)
{
frame_t frame;
@ -2907,10 +2908,10 @@ uint64_t amps_encode_overload_control(uint8_t dcc, uint8_t *olc, uint8_t end)
frame.ie[AMPS_IE_OLC_15] = olc[15];
frame.ie[AMPS_IE_END] = end;
frame.ie[AMPS_IE_OHD] = 4;
return amps_encode_word(&frame, &overload_control_global_action, -1);
return amps_encode_word(&frame, &overload_control_global_action, debug);
}
uint64_t amps_encode_access_type(uint8_t dcc, uint8_t bis, uint8_t pci_home, uint8_t pci_roam, uint8_t bspc, uint8_t bscap, uint8_t end)
uint64_t amps_encode_access_type(uint8_t dcc, uint8_t bis, uint8_t pci_home, uint8_t pci_roam, uint8_t bspc, uint8_t bscap, uint8_t end, int debug)
{
frame_t frame;
@ -2925,10 +2926,10 @@ uint64_t amps_encode_access_type(uint8_t dcc, uint8_t bis, uint8_t pci_home, uin
frame.ie[AMPS_IE_BSCAP] = bscap;
frame.ie[AMPS_IE_END] = end;
frame.ie[AMPS_IE_OHD] = 4;
return amps_encode_word(&frame, &access_type_parameters_global_action, -1);
return amps_encode_word(&frame, &access_type_parameters_global_action, debug);
}
uint64_t amps_encode_access_attempt(uint8_t dcc, uint8_t maxbusy_pgr, uint8_t maxsztr_pgr, uint8_t maxbusy_other, uint8_t maxsztr_other, uint8_t end)
uint64_t amps_encode_access_attempt(uint8_t dcc, uint8_t maxbusy_pgr, uint8_t maxsztr_pgr, uint8_t maxbusy_other, uint8_t maxsztr_other, uint8_t end, int debug)
{
frame_t frame;
@ -2942,7 +2943,7 @@ uint64_t amps_encode_access_attempt(uint8_t dcc, uint8_t maxbusy_pgr, uint8_t ma
frame.ie[AMPS_IE_MAXSZTR_OTHER] = maxsztr_other;
frame.ie[AMPS_IE_END] = end;
frame.ie[AMPS_IE_OHD] = 4;
return amps_encode_word(&frame, &access_attempt_parameters_global_action, -1);
return amps_encode_word(&frame, &access_attempt_parameters_global_action, debug);
}
static uint64_t amps_encode_word1_abbreviated_address_word(uint8_t dcc, uint32_t min1, int multiple)
@ -2956,7 +2957,7 @@ static uint64_t amps_encode_word1_abbreviated_address_word(uint8_t dcc, uint32_t
frame.ie[AMPS_IE_T1T2] = 0;
frame.ie[AMPS_IE_DCC] = dcc;
frame.ie[AMPS_IE_MIN1] = min1;
return amps_encode_word(&frame, &word1_abbreviated_address_word, DEBUG_INFO);
return amps_encode_word(&frame, &word1_abbreviated_address_word, 1);
}
static uint64_t amps_encode_word2_extended_address_word_a(uint16_t min2, uint8_t msg_type, uint8_t ordq, uint8_t order)
@ -2971,7 +2972,7 @@ static uint64_t amps_encode_word2_extended_address_word_a(uint16_t min2, uint8_t
frame.ie[AMPS_IE_LOCAL_MSG_TYPE] = msg_type;
frame.ie[AMPS_IE_ORDQ] = ordq;
frame.ie[AMPS_IE_ORDER] = order;
return amps_encode_word(&frame, &word2_extended_address_word_a, DEBUG_INFO);
return amps_encode_word(&frame, &word2_extended_address_word_a, 1);
}
static uint64_t amps_encode_word2_extended_address_word_b(uint8_t scc, uint16_t min2, uint8_t vmac, uint16_t chan)
@ -2984,7 +2985,7 @@ static uint64_t amps_encode_word2_extended_address_word_b(uint8_t scc, uint16_t
frame.ie[AMPS_IE_MIN2] = min2;
frame.ie[AMPS_IE_VMAC] = vmac;
frame.ie[AMPS_IE_CHAN] = chan;
return amps_encode_word(&frame, &word2_extended_address_word_b, DEBUG_INFO);
return amps_encode_word(&frame, &word2_extended_address_word_b, 1);
}
static uint64_t amps_encode_mobile_station_control_message_word1_a(uint8_t pscc, uint8_t msg_type, uint8_t ordq, uint8_t order)
@ -2999,7 +3000,7 @@ static uint64_t amps_encode_mobile_station_control_message_word1_a(uint8_t pscc,
frame.ie[AMPS_IE_LOCAL_MSG_TYPE] = msg_type;
frame.ie[AMPS_IE_ORDQ] = ordq;
frame.ie[AMPS_IE_ORDER] = order;
return amps_encode_word(&frame, &mobile_station_control_message_word1_a, DEBUG_INFO);
return amps_encode_word(&frame, &mobile_station_control_message_word1_a, 1);
}
static uint64_t amps_encode_mobile_station_control_message_word1_b(uint8_t scc, uint8_t pscc, uint8_t dtx, uint8_t pvi, uint8_t mem, uint8_t vmac, uint16_t chan)
@ -3016,7 +3017,7 @@ static uint64_t amps_encode_mobile_station_control_message_word1_b(uint8_t scc,
frame.ie[AMPS_IE_MEM] = mem;
frame.ie[AMPS_IE_VMAC] = vmac;
frame.ie[AMPS_IE_CHAN] = chan;
return amps_encode_word(&frame, &mobile_station_control_message_word1_b, DEBUG_INFO);
return amps_encode_word(&frame, &mobile_station_control_message_word1_b, 1);
}
/* decoder function of a word */
@ -3417,13 +3418,14 @@ static void amps_encode_fvc_bits(uint64_t word_a, char *bits)
int amps_encode_frame_focc(amps_t *amps, char *bits)
{
uint64_t word;
int debug = !amps->tx_focc_debugged;
/* init overhead train */
if (amps->tx_focc_frame_count == 0)
prepare_sysinfo(&amps->si);
/* send overhead train */
if (amps->si.num) {
word = get_sysinfo(&amps->si);
word = get_sysinfo(&amps->si, debug);
if (++amps->tx_focc_frame_count >= amps->si.overhead_repeat)
amps->tx_focc_frame_count = 0;
goto send;
@ -3474,9 +3476,12 @@ int amps_encode_frame_focc(amps_t *amps, char *bits)
}
/* send filler */
word = amps_encode_control_filler(amps, amps->si.dcc, amps->si.filler.cmac, amps->si.filler.sdcc1, amps->si.filler.sdcc2, amps->si.filler.wfom);
word = amps_encode_control_filler(amps, amps->si.dcc, amps->si.filler.cmac, amps->si.filler.sdcc1, amps->si.filler.sdcc2, amps->si.filler.wfom, debug);
if (++amps->tx_focc_frame_count >= amps->si.overhead_repeat)
amps->tx_focc_frame_count = 0;
if (debug)
PDEBUG_CHAN(DFRAME, DEBUG_INFO, "Subsequent system/filler frames are not show, to prevent flooding the output.\n");
amps->tx_focc_debugged = 1;
send:
amps_encode_focc_bits(word, word, bits);

View File

@ -211,16 +211,16 @@ typedef struct amps_frame {
} frame_t;
void init_frame(void);
uint64_t amps_encode_word1_system(uint8_t dcc, uint16_t sid1, uint8_t ep, uint8_t auth, uint8_t pci, uint8_t nawc);
uint64_t tacs_encode_word1_system(uint8_t dcc, uint16_t aid1, uint8_t ep, uint8_t auth, uint8_t pci, uint8_t nawc);
uint64_t amps_encode_word2_system(uint8_t dcc, uint8_t s, uint8_t e, uint8_t regh, uint8_t regr, uint8_t dtx, uint8_t n_1, uint8_t rcf, uint8_t cpa, uint8_t cmax_1, uint8_t end);
uint64_t amps_encode_registration_id(uint8_t dcc, uint32_t regid, uint8_t end);
uint64_t amps_encode_registration_increment(uint8_t dcc, uint16_t regincr, uint8_t end);
uint64_t amps_encode_location_area(uint8_t dcc, uint8_t pureg, uint8_t pdreg, uint8_t lreg, uint16_t locaid, uint8_t end);
uint64_t amps_encode_new_access_channel_set(uint8_t dcc, uint16_t newacc, uint8_t end);
uint64_t amps_encode_overload_control(uint8_t dcc, uint8_t *olc, uint8_t end);
uint64_t amps_encode_access_type(uint8_t dcc, uint8_t bis, uint8_t pci_home, uint8_t pci_roam, uint8_t bspc, uint8_t bscap, uint8_t end);
uint64_t amps_encode_access_attempt(uint8_t dcc, uint8_t maxbusy_pgr, uint8_t maxsztr_pgr, uint8_t maxbusy_other, uint8_t maxsztr_other, uint8_t end);
uint64_t amps_encode_word1_system(uint8_t dcc, uint16_t sid1, uint8_t ep, uint8_t auth, uint8_t pci, uint8_t nawc, int debug);
uint64_t tacs_encode_word1_system(uint8_t dcc, uint16_t aid1, uint8_t ep, uint8_t auth, uint8_t pci, uint8_t nawc, int debug);
uint64_t amps_encode_word2_system(uint8_t dcc, uint8_t s, uint8_t e, uint8_t regh, uint8_t regr, uint8_t dtx, uint8_t n_1, uint8_t rcf, uint8_t cpa, uint8_t cmax_1, uint8_t end, int debug);
uint64_t amps_encode_registration_id(uint8_t dcc, uint32_t regid, uint8_t end, int debug);
uint64_t amps_encode_registration_increment(uint8_t dcc, uint16_t regincr, uint8_t end, int debug);
uint64_t amps_encode_location_area(uint8_t dcc, uint8_t pureg, uint8_t pdreg, uint8_t lreg, uint16_t locaid, uint8_t end, int debug);
uint64_t amps_encode_new_access_channel_set(uint8_t dcc, uint16_t newacc, uint8_t end, int debug);
uint64_t amps_encode_overload_control(uint8_t dcc, uint8_t *olc, uint8_t end, int debug);
uint64_t amps_encode_access_type(uint8_t dcc, uint8_t bis, uint8_t pci_home, uint8_t pci_roam, uint8_t bspc, uint8_t bscap, uint8_t end, int debug);
uint64_t amps_encode_access_attempt(uint8_t dcc, uint8_t maxbusy_pgr, uint8_t maxsztr_pgr, uint8_t maxbusy_other, uint8_t maxsztr_other, uint8_t end, int debug);
int amps_encode_frame_focc(amps_t *amps, char *bits);
int amps_encode_frame_fvc(amps_t *amps, char *bits);
int amps_decode_frame(amps_t *amps, const char *bits, int count, double level, double quality, int negative);

View File

@ -150,7 +150,7 @@ void prepare_sysinfo(amps_si *si)
}
}
uint64_t get_sysinfo(amps_si *si)
uint64_t get_sysinfo(amps_si *si, int debug)
{
int count, nawc, end = 0;
time_t ti = time(NULL);
@ -166,27 +166,27 @@ uint64_t get_sysinfo(amps_si *si)
case SYSINFO_WORD1:
nawc = si->num - 1;
if (!tacs)
return amps_encode_word1_system(si->dcc, si->word1.sid1, si->word1.ep, si->word1.auth, si->word1.pci, nawc);
return amps_encode_word1_system(si->dcc, si->word1.sid1, si->word1.ep, si->word1.auth, si->word1.pci, nawc, debug);
else
return tacs_encode_word1_system(si->dcc, si->word1.sid1, si->word1.ep, si->word1.auth, si->word1.pci, nawc);
return tacs_encode_word1_system(si->dcc, si->word1.sid1, si->word1.ep, si->word1.auth, si->word1.pci, nawc, debug);
case SYSINFO_WORD2:
return amps_encode_word2_system(si->dcc, si->word2.s, si->word2.e, si->word2.regh, si->word2.regr, si->word2.dtx, si->word2.n_1, si->word2.rcf, si->word2.cpa, si->word2.cmax_1, end);
return amps_encode_word2_system(si->dcc, si->word2.s, si->word2.e, si->word2.regh, si->word2.regr, si->word2.dtx, si->word2.n_1, si->word2.rcf, si->word2.cpa, si->word2.cmax_1, end, debug);
case SYSINFO_REG_ID:
/* use time stamp to generate regid */
si->reg_id.regid = ti & 0xfffff;
return amps_encode_registration_id(si->dcc, si->reg_id.regid, end);
return amps_encode_registration_id(si->dcc, si->reg_id.regid, end, debug);
case SYSINFO_REG_INCR:
return amps_encode_registration_increment(si->dcc, si->reg_incr.regincr, end);
return amps_encode_registration_increment(si->dcc, si->reg_incr.regincr, end, debug);
case SYSINFO_LOC_AREA:
return amps_encode_location_area(si->dcc, si->loc_area.pureg, si->loc_area.pdreg, si->loc_area.lreg, si->loc_area.locaid, end);
return amps_encode_location_area(si->dcc, si->loc_area.pureg, si->loc_area.pdreg, si->loc_area.lreg, si->loc_area.locaid, end, debug);
case SYSINFO_NEW_ACC:
return amps_encode_new_access_channel_set(si->dcc, si->new_acc.newacc, end);
return amps_encode_new_access_channel_set(si->dcc, si->new_acc.newacc, end, debug);
case SYSINFO_OVERLOAD:
return amps_encode_overload_control(si->dcc, si->overload.olc, end);
return amps_encode_overload_control(si->dcc, si->overload.olc, end, debug);
case SYSINFO_ACC_TYPE:
return amps_encode_access_type(si->dcc, si->acc_type.bis, si->acc_type.pci_home, si->acc_type.pci_roam, si->acc_type.bspc, si->acc_type.bscap, end);
return amps_encode_access_type(si->dcc, si->acc_type.bis, si->acc_type.pci_home, si->acc_type.pci_roam, si->acc_type.bspc, si->acc_type.bscap, end, debug);
case SYSINFO_ACC_ATTEMPT:
return amps_encode_access_attempt(si->dcc, si->acc_attempt.maxbusy_pgr, si->acc_attempt.maxsztr_pgr, si->acc_attempt.maxbusy_other, si->acc_attempt.maxsztr_other, end);
return amps_encode_access_attempt(si->dcc, si->acc_attempt.maxbusy_pgr, si->acc_attempt.maxsztr_pgr, si->acc_attempt.maxbusy_other, si->acc_attempt.maxsztr_other, end, debug);
}
fprintf(stderr, "get_sysinfo unknown type, please fix!\n");

View File

@ -112,5 +112,5 @@ typedef struct system_information {
void init_sysinfo(amps_si *si, int cmac, int vmac, int dtx, int dcc, int sid1, int regh, int regr, int pureg, int pdreg, int locaid, int regincr, int bis);
void prepare_sysinfo(amps_si *si);
uint64_t get_sysinfo(amps_si *si);
uint64_t get_sysinfo(amps_si *si, int debug);

View File

@ -159,6 +159,12 @@
#define CUT_OFF_EMPHASIS_CNETZ 796.0 /* 200 uS time constant */
/* OgK list of alternative channels, NOT including 131 */
cnetz_t *ogk_list[16];
int ogk_list_count = 0;
int ogk_list_index = 0;
/* Convert channel number to frequency number of base station.
Set 'unterband' to 1 to get frequency of mobile station. */
double cnetz_kanal2freq(int kanal, int unterband)
@ -286,42 +292,38 @@ int cnetz_init(void)
}
/* Create transceiver instance and link to a list. */
int cnetz_create(const char *kanal, enum cnetz_chan_type chan_type, const char *device, int use_sdr, enum demod_type demod, int samplerate, double rx_gain, double tx_gain, int challenge_valid, uint64_t challenge, int response_valid, uint64_t response, int warteschlange, int metering, double speech_deviation, 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(const char *kanal_name, enum cnetz_chan_type chan_type, const char *device, int use_sdr, enum demod_type demod, int samplerate, double rx_gain, double tx_gain, int challenge_valid, uint64_t challenge, int response_valid, uint64_t response, int warteschlange, int metering, double speech_deviation, 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;
int kanal;
int rc;
if ((atoi(kanal) & 1) && (atoi(kanal) < 3 || atoi(kanal) > 1147)) {
PDEBUG(DCNETZ, DEBUG_ERROR, "Channel ('Kanal') number %s invalid. For odd channel numbers, use channel 3 ... 1147.\n", kanal);
kanal = atoi(kanal_name);
if ((kanal & 1) && (kanal < 3 || kanal > 1147)) {
PDEBUG(DCNETZ, DEBUG_ERROR, "Channel ('Kanal') number %d invalid. For odd channel numbers, use channel 3 ... 1147.\n", kanal);
return -EINVAL;
}
if ((atoi(kanal) & 1) && atoi(kanal) > 947) {
PDEBUG(DCNETZ, DEBUG_NOTICE, "You defined an extended frequency channel %s, only newer phones support this!\n", kanal);
if ((kanal & 1) && kanal > 947) {
PDEBUG(DCNETZ, DEBUG_NOTICE, "You defined an extended frequency channel %d, only newer phones support this!\n", kanal);
}
if (!(atoi(kanal) & 1) && (atoi(kanal) < 4 || atoi(kanal) > 918)) {
PDEBUG(DCNETZ, DEBUG_ERROR, "Channel ('Kanal') number %s invalid. For even channel numbers, use channel 4 ... 918.\n", kanal);
if (!(kanal & 1) && (kanal < 4 || kanal > 918)) {
PDEBUG(DCNETZ, DEBUG_ERROR, "Channel ('Kanal') number %d invalid. For even channel numbers, use channel 4 ... 918.\n", kanal);
return -EINVAL;
}
if (!(atoi(kanal) & 1) && atoi(kanal) > 758) {
PDEBUG(DCNETZ, DEBUG_NOTICE, "You defined an extended frequency %s, only newer phones support this!\n", kanal);
}
/* OgK must be on channel 131 */
if ((chan_type == CHAN_TYPE_OGK || chan_type == CHAN_TYPE_OGK_SPK) && atoi(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;
if (!(kanal & 1) && kanal > 758) {
PDEBUG(DCNETZ, DEBUG_NOTICE, "You defined an extended frequency channel %d, only newer phones support this!\n", kanal);
}
/* SpK must be on channel other than 131 */
if (chan_type == CHAN_TYPE_SPK && atoi(kanal) == CNETZ_OGK_KANAL) {
PDEBUG(DCNETZ, DEBUG_NOTICE, "You must not use channel %d for traffic channel!\n", CNETZ_OGK_KANAL);
if (chan_type == CHAN_TYPE_SPK && kanal == CNETZ_STD_OGK_KANAL) {
PDEBUG(DCNETZ, DEBUG_NOTICE, "You must not use channel %d for traffic channel!\n", CNETZ_STD_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);
PDEBUG(DCNETZ, DEBUG_NOTICE, "You selected channel %d ('Orga-Kanal') for combined control + traffic channel. Some phones will reject this.\n", kanal);
}
for (sender = sender_head; sender; sender = sender->next) {
@ -338,11 +340,21 @@ int cnetz_create(const char *kanal, enum cnetz_chan_type chan_type, const char *
return -ENOMEM;
}
PDEBUG(DCNETZ, DEBUG_DEBUG, "Creating 'C-Netz' instance for 'Kanal' = %s (sample rate %d).\n", kanal, samplerate);
PDEBUG(DCNETZ, DEBUG_DEBUG, "Creating 'C-Netz' instance for 'Kanal' = %d (sample rate %d).\n", kanal, samplerate);
cnetz->kanal = kanal;
if ((chan_type == CHAN_TYPE_OGK || chan_type == CHAN_TYPE_OGK_SPK) && kanal != CNETZ_STD_OGK_KANAL) {
if (ogk_list_count == 16) {
PDEBUG(DCNETZ, DEBUG_ERROR, "No more than 16 non-standard OGK are allowed!\n");
rc = -EINVAL;
goto error;
}
ogk_list[ogk_list_count++] = cnetz;
}
/* 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, kanal, cnetz_kanal2freq(atoi(kanal), 0), cnetz_kanal2freq(atoi(kanal), 1), device, use_sdr, samplerate, rx_gain, tx_gain, 0, 0, write_rx_wave, write_tx_wave, read_rx_wave, read_tx_wave, loopback, PAGING_SIGNAL_NONE);
rc = sender_create(&cnetz->sender, kanal_name, cnetz_kanal2freq(kanal, 0), cnetz_kanal2freq(kanal, 1), device, use_sdr, samplerate, rx_gain, tx_gain, 0, 0, write_rx_wave, write_tx_wave, read_rx_wave, read_tx_wave, loopback, PAGING_SIGNAL_NONE);
if (rc < 0) {
PDEBUG(DCNETZ, DEBUG_ERROR, "Failed to init transceiver process!\n");
goto error;
@ -403,13 +415,13 @@ int cnetz_create(const char *kanal, enum cnetz_chan_type chan_type, const char *
cnetz_go_idle(cnetz);
#ifdef DEBUG_SPK
transaction_t *trans = create_transaction(cnetz, TRANS_DS, 2, 2, 22002, -1, -1);
transaction_t *trans = create_transaction(cnetz, TRANS_DS, 2, 2, 22002, -1, -1, NAN);
trans->mo_call = 1;
cnetz_set_sched_dsp_mode(cnetz, DSP_MODE_SPK_K, (cnetz->sched_ts + 2) & 31);
#else
/* create transaction for speech channel loopback */
if (loopback && chan_type == CHAN_TYPE_SPK) {
transaction_t *trans = create_transaction(cnetz, TRANS_VHQ_K, 2, 2, 22002, -1, -1);
transaction_t *trans = create_transaction(cnetz, TRANS_VHQ_K, 2, 2, 22002, -1, -1, NAN);
trans->mo_call = 1;
cnetz_set_dsp_mode(cnetz, DSP_MODE_SPK_K);
cnetz_set_sched_dsp_mode(cnetz, DSP_MODE_SPK_K, (cnetz->sched_ts + 1) & 31);
@ -419,23 +431,23 @@ int cnetz_create(const char *kanal, enum cnetz_chan_type chan_type, const char *
#if 0
/* debug flushing transactions */
transaction_t *trans1, *trans2;
trans1 = create_transaction(cnetz, 99, 6, 2, 15784, -1, -1);
trans1 = create_transaction(cnetz, 99, 6, 2, 15784, -1, -1, NAN);
destroy_transaction(trans1);
trans1 = create_transaction(cnetz, 99, 6, 2, 15784, -1, -1);
trans1 = create_transaction(cnetz, 99, 6, 2, 15784, -1, -1, NAN);
destroy_transaction(trans1);
trans1 = create_transaction(cnetz, 99, 6, 2, 15784, -1, -1);
trans2 = create_transaction(cnetz, 99, 2, 2, 22002, -1, -1);
trans1 = create_transaction(cnetz, 99, 6, 2, 15784, -1, -1, NAN);
trans2 = create_transaction(cnetz, 99, 2, 2, 22002, -1, -1, NAN);
unlink_transaction(trans1);
link_transaction(trans1, cnetz);
cnetz_flush_other_transactions(cnetz, trans1);
trans2 = create_transaction(cnetz, 99, 2, 2, 22002, -1, -1);
trans2 = create_transaction(cnetz, 99, 2, 2, 22002, -1, -1, NAN);
cnetz_flush_other_transactions(cnetz, trans2);
#endif
PDEBUG(DCNETZ, DEBUG_NOTICE, "Created 'Kanal' #%s of type '%s' = %s\n", kanal, chan_type_short_name(chan_type), chan_type_long_name(chan_type));
const char *name, *station;
name = get_station_name(si.fuz_nat, si.fuz_fuvst, si.fuz_rest, &station);
PDEBUG(DNMT, DEBUG_NOTICE, " -> Using cell ID: Nat=%d FuVst=%d Rest=%d Name='%s' (%s)\n", si.fuz_nat, si.fuz_fuvst, si.fuz_rest, name, station);
PDEBUG(DCNETZ, DEBUG_NOTICE, "Created 'Kanal' #%d of type '%s' = %s\n", kanal, chan_type_short_name(chan_type), chan_type_long_name(chan_type));
const char *name, *long_name;
name = get_station_name(si.fuz_nat, si.fuz_fuvst, si.fuz_rest, &long_name);
PDEBUG(DCNETZ, DEBUG_NOTICE, " -> Using cell ID: Nat=%d FuVst=%d Rest=%d Name='%s' Long Name='%s'\n", si.fuz_nat, si.fuz_fuvst, si.fuz_rest, name, long_name);
return 0;
@ -473,9 +485,9 @@ static cnetz_t *search_free_spk(int extended)
cnetz = (cnetz_t *) sender;
/* ignore extended frequency, if not supported */
if (!extended) {
if ((atoi(sender->kanal) & 1) && atoi(sender->kanal) > 947)
if ((cnetz->kanal & 1) && cnetz->kanal > 947)
continue;
if (!(atoi(sender->kanal) & 1) && atoi(sender->kanal) > 758)
if (!(cnetz->kanal & 1) && cnetz->kanal > 758)
continue;
}
/* ignore busy channel */
@ -492,7 +504,7 @@ static cnetz_t *search_free_spk(int extended)
return ogk_spk;
}
static cnetz_t *search_ogk(void)
static cnetz_t *search_ogk(int kanal)
{
sender_t *sender;
cnetz_t *cnetz;
@ -502,6 +514,8 @@ static cnetz_t *search_ogk(void)
/* ignore busy channel */
if (cnetz->state != CNETZ_IDLE)
continue;
if (cnetz->kanal != kanal)
continue;
if (cnetz->chan_type == CHAN_TYPE_OGK)
return cnetz;
if (cnetz->chan_type == CHAN_TYPE_OGK_SPK)
@ -514,7 +528,6 @@ static cnetz_t *search_ogk(void)
/* 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)
@ -529,25 +542,26 @@ void cnetz_go_idle(cnetz_t *cnetz)
PDEBUG(DCNETZ, DEBUG_INFO, "Entering IDLE state on channel %s.\n", cnetz->sender.kanal);
cnetz_new_state(cnetz, CNETZ_IDLE);
cnetz->sched_lr_debugged = 0;
cnetz->sched_mlr_debugged = 0;
/* set scheduler to OgK or turn off SpK */
if (cnetz->dsp_mode == DSP_MODE_SPK_K || cnetz->dsp_mode == DSP_MODE_SPK_V) {
/* switch next frame after distributed signaling boundary (multiple of 8 slots) */
cnetz_set_sched_dsp_mode(cnetz, (atoi(cnetz->sender.kanal) == CNETZ_OGK_KANAL) ? DSP_MODE_OGK : DSP_MODE_OFF, (cnetz->sched_ts + 8) & 24);
cnetz_set_sched_dsp_mode(cnetz, (cnetz->chan_type == CHAN_TYPE_OGK || cnetz->chan_type == CHAN_TYPE_OGK_SPK) ? DSP_MODE_OGK : DSP_MODE_OFF, (cnetz->sched_ts + 8) & 24);
} else {
/* switch next frame */
cnetz_set_sched_dsp_mode(cnetz, (atoi(cnetz->sender.kanal) == CNETZ_OGK_KANAL) ? DSP_MODE_OGK : DSP_MODE_OFF, (cnetz->sched_ts + 1) & 31);
cnetz_set_dsp_mode(cnetz, (atoi(cnetz->sender.kanal) == CNETZ_OGK_KANAL) ? DSP_MODE_OGK : DSP_MODE_OFF);
cnetz_set_sched_dsp_mode(cnetz, (cnetz->chan_type == CHAN_TYPE_OGK || cnetz->chan_type == CHAN_TYPE_OGK_SPK) ? DSP_MODE_OGK : DSP_MODE_OFF, (cnetz->sched_ts + 1) & 31);
cnetz_set_dsp_mode(cnetz, (cnetz->chan_type == CHAN_TYPE_OGK || cnetz->chan_type == CHAN_TYPE_OGK_SPK) ? 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);
trans = search_transaction_queue();
if (trans) {
PDEBUG(DCNETZ, DEBUG_NOTICE, "Now channel available for queued subscriber '%s'.\n", transaction2rufnummer(trans));
PDEBUG(DCNETZ, DEBUG_NOTICE, "Now channel is 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);
timer_start(&trans->timer, 3.0); /* Wait at least one frame cycles until timeout */
}
}
@ -585,14 +599,14 @@ void call_down_clock(void) {}
int call_down_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;
cnetz_t *ogk, *spk;
int rc;
int extended;
transaction_t *trans;
uint8_t futln_nat;
uint8_t futln_fuvst;
int futln_rest; /* use int for checking size > 65535 */
int ogk_kanal;
/* 1. split number into elements */
futln_nat = dialing[0] - '0';
@ -605,28 +619,22 @@ int call_down_setup(int callref, const char __attribute__((unused)) *caller_id,
}
/* 2. check if the subscriber is attached */
rc = find_db(futln_nat, futln_fuvst, futln_rest, NULL, &extended);
rc = find_db(futln_nat, futln_fuvst, futln_rest, &ogk_kanal, NULL, &extended);
if (rc < 0) {
PDEBUG(DCNETZ, DEBUG_NOTICE, "Outgoing call to not attached subscriber, rejecting!\n");
return -CAUSE_OUTOFORDER;
}
/* 3. check if given number is already in a call, return BUSY */
for (sender = sender_head; sender; sender = sender->next) {
cnetz = (cnetz_t *) sender;
/* search transaction for this number */
trans = search_transaction_number(cnetz, futln_nat, futln_fuvst, futln_rest);
if (trans)
break;
}
if (sender) {
trans = search_transaction_number_global(futln_nat, futln_fuvst, futln_rest);
if (trans) {
PDEBUG(DCNETZ, DEBUG_NOTICE, "Outgoing call to busy number, rejecting!\n");
return -CAUSE_BUSY;
}
/* 4. check if we have no OgK, return NOCHANNEL */
cnetz = search_ogk();
if (!cnetz) {
ogk = search_ogk(ogk_kanal);
if (!ogk) {
PDEBUG(DCNETZ, DEBUG_NOTICE, "Outgoing call, but OgK is currently busy, rejecting!\n");
return -CAUSE_NOCHANNEL;
}
@ -634,17 +642,17 @@ int call_down_setup(int callref, const char __attribute__((unused)) *caller_id,
/* 5. check if all senders are busy, return NOCHANNEL */
spk = search_free_spk(extended);
if (!spk) {
if (!cnetz->warteschlange) {
if (!ogk->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);
PDEBUG(DCNETZ, DEBUG_INFO, "Call to mobile station, paging station id '%s'\n", dialing);
/* 6. trying to page mobile station */
trans = create_transaction(cnetz, (spk) ? TRANS_VAK : TRANS_WSK, futln_nat, futln_fuvst, futln_rest, -1, -1);
trans = create_transaction(ogk, (spk) ? TRANS_VAK : TRANS_WSK, futln_nat, futln_fuvst, futln_rest, -1, -1, NAN);
if (!trans) {
PDEBUG(DCNETZ, DEBUG_ERROR, "Failed to create transaction\n");
return -CAUSE_TEMPFAIL;
@ -750,17 +758,17 @@ void call_down_release(int callref, int cause)
}
}
int cnetz_meldeaufruf(uint8_t futln_nat, uint8_t futln_fuvst, uint16_t futln_rest)
int cnetz_meldeaufruf(uint8_t futln_nat, uint8_t futln_fuvst, uint16_t futln_rest, int ogk_kanal)
{
cnetz_t *cnetz;
transaction_t *trans;
cnetz = search_ogk();
cnetz = search_ogk(ogk_kanal);
if (!cnetz) {
PDEBUG(DCNETZ, DEBUG_NOTICE, "'Meldeaufruf', but OgK is currently busy!\n");
return -CAUSE_NOCHANNEL;
}
trans = create_transaction(cnetz, TRANS_MA, futln_nat, futln_fuvst, futln_rest, -1, -1);
trans = create_transaction(cnetz, TRANS_MA, futln_nat, futln_fuvst, futln_rest, -1, -1, NAN);
if (!trans) {
PDEBUG(DCNETZ, DEBUG_ERROR, "Failed to create transaction\n");
return -CAUSE_TEMPFAIL;
@ -833,15 +841,6 @@ void transaction_timeout(struct timer *timer)
cnetz_t *cnetz = trans->cnetz;
switch (trans->state) {
case TRANS_WAF:
PDEBUG_CHAN(DCNETZ, DEBUG_NOTICE, "No response after dialing request 'Wahlaufforderung'\n");
if (trans->try == N) {
trans_new_state(trans, TRANS_WBN);
break;
}
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_up_release(trans->callref, CAUSE_NOCHANNEL);
@ -909,12 +908,6 @@ void transaction_timeout(struct timer *timer)
trans->callref = 0;
cnetz_release(trans, CNETZ_CAUSE_FUNKTECHNISCH);
break;
case TRANS_MFT:
PDEBUG_CHAN(DCNETZ, DEBUG_NOTICE, "No response after keepalive order 'Meldeaufruf'\n");
/* no response to availability check */
trans->page_failed = 1;
destroy_transaction(trans);
break;
default:
PDEBUG_CHAN(DCNETZ, DEBUG_ERROR, "Timeout unhandled in state %" PRIu64 "\n", trans->state);
}
@ -984,6 +977,7 @@ const telegramm_t *cnetz_transmit_telegramm_rufblock(cnetz_t *cnetz)
telegramm.fuz_fuvst_nr = si.fuz_fuvst;
telegramm.fuz_rest_nr = si.fuz_rest;
telegramm.kennung_fufst = si.kennung_fufst;
telegramm.bahn_bs = si.bahn_bs;
telegramm.nachbarschafts_prioritaets_bit = si.nachbar_prio;
telegramm.bewertung_nach_pegel_und_entfernung = si.bewertung;
telegramm.entfernungsangabe_der_fufst = si.entfernung;
@ -992,7 +986,7 @@ const telegramm_t *cnetz_transmit_telegramm_rufblock(cnetz_t *cnetz)
telegramm.grenzwert_fuer_umschalten = si.grenz_umschalten;
telegramm.grenze_fuer_ausloesen = si.grenz_ausloesen;
trans = search_transaction(cnetz, TRANS_EM | TRANS_UM | TRANS_WBN | TRANS_WBP | TRANS_VAG | TRANS_VAK | TRANS_ATQ | TRANS_VA | TRANS_WSK);
trans = search_transaction(cnetz, TRANS_EM | TRANS_UM | TRANS_WBN | TRANS_WBP | TRANS_VAG | TRANS_VAK | TRANS_ATQ | TRANS_ATQ_IDLE | TRANS_VA | TRANS_WSK);
if (trans) {
telegramm.futln_nationalitaet = trans->futln_nat;
telegramm.futln_heimat_fuvst_nr = trans->futln_fuvst;
@ -1057,14 +1051,14 @@ vak:
break;
}
if (spk == cnetz) {
PDEBUG(DCNETZ, DEBUG_INFO, "Staying on combined calling + traffic channel %s\n", spk->sender.kanal);
PDEBUG(DCNETZ, DEBUG_INFO, "Staying on combined control + traffic channel %s\n", spk->sender.kanal);
} else {
PDEBUG(DCNETZ, DEBUG_INFO, "Assigning phone to traffic channel %s\n", spk->sender.kanal);
/* sync RX time to current OgK time */
fsk_copy_sync(&spk->fsk_demod, &cnetz->fsk_demod);
}
/* set channel */
telegramm.frequenz_nr = atoi(spk->sender.kanal);
telegramm.frequenz_nr = spk->kanal;
/* change state to busy */
cnetz_new_state(spk, CNETZ_BUSY);
/* schedule switching two slots ahead */
@ -1076,6 +1070,7 @@ vak:
cnetz_flush_other_transactions(spk, trans);
break;
case TRANS_ATQ:
case TRANS_ATQ_IDLE:
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);
@ -1114,15 +1109,32 @@ const telegramm_t *cnetz_transmit_telegramm_meldeblock(cnetz_t *cnetz)
memset(&telegramm, 0, sizeof(telegramm));
telegramm.opcode = OPCODE_MLR_M;
telegramm.max_sendeleistung = cnetz_power2bits(cnetz->ms_power);
telegramm.ogk_verkehrsanteil = 0; /* must be 0 or phone might not respond to messages in different slot */
telegramm.ogk_verkehrsanteil = 0; /* must be 0 or some phone might not respond to messages in different slots */
telegramm.teilnehmergruppensperre = si.teilnehmergruppensperre;
telegramm.anzahl_gesperrter_teilnehmergruppen = si.anzahl_gesperrter_teilnehmergruppen;
telegramm.ogk_vorschlag = CNETZ_OGK_KANAL;
if (ogk_list_count) {
/* if we have alternative OGKs, we cycle through the list and indicate their channels */
telegramm.ogk_vorschlag = ogk_list[ogk_list_index]->kanal;
if (++ogk_list_index == ogk_list_count)
ogk_list_index = 0;
} else
telegramm.ogk_vorschlag = CNETZ_STD_OGK_KANAL;
telegramm.fuz_rest_nr = si.fuz_rest;
trans = search_transaction(cnetz, TRANS_VWG | TRANS_MA);
next_candidate:
trans = search_transaction(cnetz, TRANS_VWG | TRANS_WAF | TRANS_MA | TRANS_MFT);
if (trans) {
switch (trans->state) {
case TRANS_WAF:
/* no response to dial request (try again or drop connection) */
PDEBUG_CHAN(DCNETZ, DEBUG_NOTICE, "No response after dialing request 'Wahlaufforderung'\n");
if (trans->try == N) {
trans_new_state(trans, TRANS_WBN);
goto next_candidate;
}
trans->try++;
trans_new_state(trans, TRANS_VWG);
/* FALLTHRU */
case TRANS_VWG:
PDEBUG_CHAN(DCNETZ, DEBUG_INFO, "Sending acknowledgment 'Wahlaufforderung' to outging call\n");
telegramm.opcode = OPCODE_WAF_M;
@ -1130,7 +1142,6 @@ const telegramm_t *cnetz_transmit_telegramm_meldeblock(cnetz_t *cnetz)
telegramm.futln_heimat_fuvst_nr = trans->futln_fuvst;
telegramm.futln_rest_nr = trans->futln_rest;
trans_new_state(trans, TRANS_WAF);
timer_start(&trans->timer, 1.0); /* Wait two slot cycles until resending */
break;
case TRANS_MA:
PDEBUG_CHAN(DCNETZ, DEBUG_INFO, "Sending keepalive request 'Meldeaufruf'\n");
@ -1139,8 +1150,13 @@ const telegramm_t *cnetz_transmit_telegramm_meldeblock(cnetz_t *cnetz)
telegramm.futln_heimat_fuvst_nr = trans->futln_fuvst;
telegramm.futln_rest_nr = trans->futln_rest;
trans_new_state(trans, TRANS_MFT);
timer_start(&trans->timer, 1.0); /* Wait two slot cycles until timeout */
break;
case TRANS_MFT:
/* no response to availability check */
PDEBUG_CHAN(DCNETZ, DEBUG_NOTICE, "No response after keepalive order 'Meldeaufruf'\n");
trans->page_failed = 1;
destroy_transaction(trans);
goto next_candidate;
default:
; /* MLR */
}
@ -1171,11 +1187,12 @@ void cnetz_receive_telegramm_ogk(cnetz_t *cnetz, telegramm_t *telegramm, int blo
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, telegramm->chipkarten_futelg_bit, telegramm->erweitertes_frequenzbandbit);
trans = create_transaction(cnetz, TRANS_EM, telegramm->futln_nationalitaet, telegramm->futln_heimat_fuvst_nr, telegramm->futln_rest_nr, telegramm->chipkarten_futelg_bit, telegramm->erweitertes_frequenzbandbit, cnetz->rf_level_db);
if (!trans) {
PDEBUG(DCNETZ, DEBUG_ERROR, "Failed to create transaction\n");
break;
}
cnetz = trans->cnetz; /* cnetz may change, due to stronger reception on different OgK */
valid_frame = 1;
break;
case OPCODE_UM_R:
@ -1192,11 +1209,12 @@ void cnetz_receive_telegramm_ogk(cnetz_t *cnetz, telegramm_t *telegramm, int blo
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, telegramm->chipkarten_futelg_bit, telegramm->erweitertes_frequenzbandbit);
trans = create_transaction(cnetz, TRANS_UM, telegramm->futln_nationalitaet, telegramm->futln_heimat_fuvst_nr, telegramm->futln_rest_nr, telegramm->chipkarten_futelg_bit, telegramm->erweitertes_frequenzbandbit, cnetz->rf_level_db);
if (!trans) {
PDEBUG(DCNETZ, DEBUG_ERROR, "Failed to create transaction\n");
break;
}
cnetz = trans->cnetz; /* cnetz may change, due to stronger reception on different OgK */
valid_frame = 1;
break;
case OPCODE_UWG_R:
@ -1208,29 +1226,33 @@ void cnetz_receive_telegramm_ogk(cnetz_t *cnetz, telegramm_t *telegramm, int blo
break;
case OPCODE_VWG_R:
case OPCODE_SRG_R:
case OPCODE_NUG_R:
if (!match_fuz(telegramm))
break;
rufnummer = telegramm2rufnummer(telegramm);
if (opcode == OPCODE_VWG_R)
PDEBUG_CHAN(DCNETZ, DEBUG_INFO, "Received outgoing Call 'Verbindungswunsch gehend' message from Subscriber '%s'\n", rufnummer);
else
else if (opcode == OPCODE_SRG_R)
PDEBUG_CHAN(DCNETZ, DEBUG_INFO, "Received outgoing emergency Call 'Sonderruf gehend' message from Subscriber '%s'\n", rufnummer);
else
PDEBUG_CHAN(DCNETZ, DEBUG_INFO, "Received outgoing Call 'Verbindungswunsch gehend bei Nachbarschaftsunterstuetzung' message from Subscriber '%s'\n", rufnummer);
if (cnetz->state != CNETZ_IDLE) {
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, -1, telegramm->erweitertes_frequenzbandbit);
trans = create_transaction(cnetz, TRANS_VWG, telegramm->futln_nationalitaet, telegramm->futln_heimat_fuvst_nr, telegramm->futln_rest_nr, -1, telegramm->erweitertes_frequenzbandbit, cnetz->rf_level_db);
if (!trans) {
PDEBUG(DCNETZ, DEBUG_ERROR, "Failed to create transaction\n");
break;
}
cnetz = trans->cnetz; /* cnetz may change, due to stronger reception on different OgK */
trans->try = 1;
valid_frame = 1;
break;
case OPCODE_WUE_M:
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");
PDEBUG_CHAN(DCNETZ, DEBUG_NOTICE, "Received dialing digits 'Wahluebertragung' message without transaction (on this OgK), ignoring!\n");
break;
}
rufnummer = transaction2rufnummer(trans);
@ -1246,17 +1268,30 @@ void cnetz_receive_telegramm_ogk(cnetz_t *cnetz, telegramm_t *telegramm, int blo
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);
trans = search_transaction_number_global(telegramm->futln_nationalitaet, telegramm->futln_heimat_fuvst_nr, telegramm->futln_rest_nr);
if (!trans) {
PDEBUG_CHAN(DCNETZ, DEBUG_NOTICE, "There is no transaction, so we assume that the phone did not receive previous release.\n");
/* 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, -1, -1);
trans = create_transaction(cnetz, TRANS_ATQ_IDLE, telegramm->futln_nationalitaet, telegramm->futln_heimat_fuvst_nr, telegramm->futln_rest_nr, -1, -1, cnetz->rf_level_db);
if (!trans) {
PDEBUG(DCNETZ, DEBUG_ERROR, "Failed to create transaction\n");
break;
}
cnetz = trans->cnetz; /* cnetz may change, due to stronger reception on different OgK */
} else {
timer_stop(&trans->timer);
trans_new_state(trans, TRANS_ATQ);
if (cnetz == trans->cnetz) {
timer_stop(&trans->timer);
trans_new_state(trans, TRANS_ATQ);
} else
if (trans->state == TRANS_ATQ_IDLE) {
trans = create_transaction(cnetz, TRANS_ATQ_IDLE, telegramm->futln_nationalitaet, telegramm->futln_heimat_fuvst_nr, telegramm->futln_rest_nr, -1, -1, cnetz->rf_level_db);
if (!trans) {
PDEBUG(DCNETZ, DEBUG_ERROR, "Failed to create transaction\n");
break;
}
cnetz = trans->cnetz; /* cnetz may change, due to stronger reception on different OgK */
} else
PDEBUG_CHAN(DCNETZ, DEBUG_NOTICE, "Received release 'Ausloesen des FuTelG im OgK-Betrieb bei WS' message without transaction (on this OgK), ignoring!\n");
}
valid_frame = 1;
break;
@ -1265,7 +1300,7 @@ void cnetz_receive_telegramm_ogk(cnetz_t *cnetz, telegramm_t *telegramm, int blo
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");
PDEBUG_CHAN(DCNETZ, DEBUG_NOTICE, "Received acknowledge 'Meldung Funktelefonteilnehmer' message without transaction (on this OgK), ignoring!\n");
break;
}
rufnummer = transaction2rufnummer(trans);
@ -1295,7 +1330,9 @@ const telegramm_t *cnetz_transmit_telegramm_spk_k(cnetz_t *cnetz)
{
static telegramm_t telegramm;
transaction_t *trans = cnetz->trans_list;
int ogk_kanal;
cnetz_t *ogk;
int rc;
if (!trans)
return NULL;
@ -1311,9 +1348,10 @@ const telegramm_t *cnetz_transmit_telegramm_spk_k(cnetz_t *cnetz)
telegramm.futln_nationalitaet = trans->futln_nat;
telegramm.futln_heimat_fuvst_nr = trans->futln_fuvst;
telegramm.futln_rest_nr = trans->futln_rest;
telegramm.frequenz_nr = atoi(cnetz->sender.kanal);
telegramm.frequenz_nr = cnetz->kanal;
telegramm.bedingte_genauigkeit_der_fufst = si.genauigkeit;
telegramm.zufallszahl = cnetz->challenge;
telegramm.bahn_bs = si.bahn_bs;
switch (trans->state) {
case TRANS_BQ:
@ -1426,9 +1464,15 @@ no_auth:
/* idle channel */
cnetz_go_idle(cnetz);
/* alloc ogk again */
ogk = search_ogk();
rc = find_db(trans->futln_nat, trans->futln_fuvst, trans->futln_rest, &ogk_kanal, NULL, NULL);
if (rc < 0) {
PDEBUG(DCNETZ, DEBUG_NOTICE, "Cannot find subscriber in database anymore, releasing!\n");
goto no_ogk;
}
ogk = search_ogk(ogk_kanal);
if (!ogk) {
PDEBUG(DCNETZ, DEBUG_NOTICE, "Cannot retry, because currently no OgK available (busy)\n");
no_ogk:
cnetz_release(trans, CNETZ_CAUSE_FUNKTECHNISCH);
if (trans->callref)
call_up_release(trans->callref, CAUSE_NOCHANNEL);
@ -1634,7 +1678,7 @@ const telegramm_t *cnetz_transmit_telegramm_spk_v(cnetz_t *cnetz)
telegramm.futln_nationalitaet = trans->futln_nat;
telegramm.futln_heimat_fuvst_nr = trans->futln_fuvst;
telegramm.futln_rest_nr = trans->futln_rest;
telegramm.frequenz_nr = atoi(cnetz->sender.kanal);
telegramm.frequenz_nr = cnetz->kanal;
telegramm.entfernung = si.entfernung;
telegramm.bedingte_genauigkeit_der_fufst = si.genauigkeit;
telegramm.gueltigkeit_des_gebuehrenstandes = 0;

View File

@ -6,7 +6,7 @@ typedef struct cnetz cnetz_t;
#include "fsk_demod.h"
#include "transaction.h"
#define CNETZ_OGK_KANAL 131
#define CNETZ_STD_OGK_KANAL 131
/* dsp modes of transmission */
enum dsp_mode {
@ -66,6 +66,7 @@ struct clock_speed {
/* instance of cnetz sender */
struct cnetz {
sender_t sender;
int kanal; /* channel number */
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 */
@ -96,9 +97,12 @@ struct cnetz {
int sched_r_m; /* Rufblock (0) / Meldeblock (1) */
enum dsp_mode sched_dsp_mode; /* what mode shall be switched to */
int sched_dsp_mode_ts; /* time slot when to switch mode (-1 = don't switch) */
int sched_lr_debugged; /* indicator to prevent debugging all idle frames */
int sched_mlr_debugged; /* indicator to prevent debugging all idle frames */
/* dsp states */
enum dsp_mode dsp_mode; /* current mode: audio, "Telegramm", .... */
double rf_level_db; /* current RF level or nan, if not applicable */
iir_filter_t lp; /* low pass filter to eliminate noise above 5280 Hz */
fsk_fm_demod_t fsk_demod; /* demod process */
double fsk_deviation; /* deviation of FSK signal on sound card */
@ -144,7 +148,7 @@ int cnetz_create(const char *kanal, enum cnetz_chan_type chan_type, const char *
void cnetz_destroy(sender_t *sender);
void cnetz_go_idle(cnetz_t *cnetz);
void cnetz_sync_frame(cnetz_t *cnetz, double sync, int ts);
int cnetz_meldeaufruf(uint8_t futln_nat, uint8_t futln_fuvst, uint16_t futln_rest);
int cnetz_meldeaufruf(uint8_t futln_nat, uint8_t futln_fuvst, uint16_t futln_rest, int ogk_kanal);
const struct telegramm *cnetz_transmit_telegramm_rufblock(cnetz_t *cnetz);
const struct telegramm *cnetz_transmit_telegramm_meldeblock(cnetz_t *cnetz);
void cnetz_receive_telegramm_ogk(cnetz_t *cnetz, struct telegramm *telegramm, int block);

View File

@ -25,18 +25,18 @@
#include "../libdebug/debug.h"
#include "cnetz.h"
#include "database.h"
#include "sysinfo.h"
/* the network specs say: check every 1 - 6.5 minutes for availability
* remove from database after 3 subsequent failures
* the phone will register 20 minutes after no call / no paging from network.
*/
#define MELDE_INTERVAL 120.0
#define MELDE_WIEDERHOLUNG 60.0
#define MELDE_MAXIMAL 3
#define MELDE_WIEDERHOLUNG 60.0 /* when busy */
typedef struct cnetz_database {
struct cnetz_database *next;
int ogk_kanal; /* available on which channel */
uint8_t futln_nat; /* who ... */
uint8_t futln_fuvst;
uint16_t futln_rest;
@ -51,6 +51,17 @@ typedef struct cnetz_database {
cnetz_db_t *cnetz_db_head;
static const char *print_meldeaufrufe(int versuche)
{
static char text[32];
if (versuche <= 0)
return "infinite";
sprintf(text, "%d", versuche);
return text;
}
/* destroy transaction */
static void remove_db(cnetz_db_t *db)
{
@ -81,19 +92,19 @@ static void db_timeout(struct timer *timer)
PDEBUG(DDB, DEBUG_INFO, "Check, if subscriber '%d,%d,%05d' is still available.\n", db->futln_nat, db->futln_fuvst, db->futln_rest);
rc = cnetz_meldeaufruf(db->futln_nat, db->futln_fuvst, db->futln_rest);
rc = cnetz_meldeaufruf(db->futln_nat, db->futln_fuvst, db->futln_rest, db->ogk_kanal);
if (rc < 0) {
/* OgK is used for speech, but this never happens in a real
* network. We just assume that the phone has responded and
* assume we had a response. */
PDEBUG(DDB, DEBUG_INFO, "OgK busy, so we assume a positive response.\n");
timer_start(&db->timer, MELDE_INTERVAL); /* when to check avaiability again */
timer_start(&db->timer, si.meldeinterval); /* when to check avaiability again */
db->retry = 0;
}
}
/* create/update db entry */
int update_db(cnetz_t __attribute__((unused)) *cnetz, uint8_t futln_nat, uint8_t futln_fuvst, uint16_t futln_rest, int *futelg_bit, int *extended, int busy, int failed)
int update_db(uint8_t futln_nat, uint8_t futln_fuvst, uint16_t futln_rest, int ogk_kanal, int *futelg_bit, int *extended, int busy, int failed)
{
cnetz_db_t *db, **dbp;
@ -128,6 +139,9 @@ int update_db(cnetz_t __attribute__((unused)) *cnetz, uint8_t futln_nat, uint8_t
PDEBUG(DDB, DEBUG_INFO, "Adding subscriber '%d,%d,%05d' to database.\n", db->futln_nat, db->futln_fuvst, db->futln_rest);
}
if (ogk_kanal)
db->ogk_kanal = ogk_kanal;
if (futelg_bit && *futelg_bit >= 0)
db->futelg_bit = *futelg_bit;
@ -136,23 +150,23 @@ int update_db(cnetz_t __attribute__((unused)) *cnetz, uint8_t futln_nat, uint8_t
db->busy = busy;
if (busy) {
PDEBUG(DDB, DEBUG_INFO, "Subscriber '%d,%d,%05d' busy now.\n", db->futln_nat, db->futln_fuvst, db->futln_rest);
PDEBUG(DDB, DEBUG_INFO, "Subscriber '%d,%d,%05d' on OGK channel #%d is busy now.\n", db->futln_nat, db->futln_fuvst, db->futln_rest, db->ogk_kanal);
timer_stop(&db->timer);
} else if (!failed) {
PDEBUG(DDB, DEBUG_INFO, "Subscriber '%d,%d,%05d' idle now.\n", db->futln_nat, db->futln_fuvst, db->futln_rest);
timer_start(&db->timer, MELDE_INTERVAL); /* when to check avaiability (again) */
PDEBUG(DDB, DEBUG_INFO, "Subscriber '%d,%d,%05d' on OGK channel #%d is idle now.\n", db->futln_nat, db->futln_fuvst, db->futln_rest, db->ogk_kanal);
timer_start(&db->timer, si.meldeinterval); /* when to check avaiability (again) */
db->retry = 0;
db->eingebucht = 1;
db->last_seen = get_time();
} else {
db->retry++;
PDEBUG(DDB, DEBUG_NOTICE, "Paging subscriber '%d,%d,%05d' failed (try %d of %d).\n", db->futln_nat, db->futln_fuvst, db->futln_rest, db->retry, MELDE_MAXIMAL);
if (db->retry == MELDE_MAXIMAL) {
PDEBUG(DDB, DEBUG_NOTICE, "Paging subscriber '%d,%d,%05d' on OGK channel #%d failed (try %d of %s).\n", db->futln_nat, db->futln_fuvst, db->futln_rest, db->ogk_kanal, db->retry, print_meldeaufrufe(si.meldeaufrufe));
if (si.meldeaufrufe && db->retry == si.meldeaufrufe) {
PDEBUG(DDB, DEBUG_INFO, "Marking subscriber as gone.\n");
db->eingebucht = 0;
return db->extended;
}
timer_start(&db->timer, MELDE_WIEDERHOLUNG); /* when to do retry */
timer_start(&db->timer, (si.meldeinterval < MELDE_WIEDERHOLUNG) ? si.meldeinterval : MELDE_WIEDERHOLUNG); /* when to do retry */
}
if (futelg_bit)
@ -162,7 +176,7 @@ int update_db(cnetz_t __attribute__((unused)) *cnetz, uint8_t futln_nat, uint8_t
return 0;
}
int find_db(uint8_t futln_nat, uint8_t futln_fuvst, uint16_t futln_rest, int *futelg_bit, int *extended)
int find_db(uint8_t futln_nat, uint8_t futln_fuvst, uint16_t futln_rest, int *ogk_kanal, int *futelg_bit, int *extended)
{
cnetz_db_t *db = cnetz_db_head;
@ -171,6 +185,8 @@ int find_db(uint8_t futln_nat, uint8_t futln_fuvst, uint16_t futln_rest, int *fu
&& db->futln_nat == futln_nat
&& db->futln_fuvst == futln_fuvst
&& db->futln_rest == futln_rest) {
if (ogk_kanal)
*ogk_kanal = db->ogk_kanal;
if (futelg_bit)
*futelg_bit = db->futelg_bit;
if (extended)
@ -193,6 +209,7 @@ void dump_db(void)
cnetz_db_t *db = cnetz_db_head;
double now = get_time();
int last;
char attached[16];
PDEBUG(DDB, DEBUG_NOTICE, "Dump of subscriber database:\n");
if (!db) {
@ -204,7 +221,8 @@ void dump_db(void)
PDEBUG(DDB, DEBUG_NOTICE, "-------------------------------------------------------------------------------\n");
while (db) {
last = (db->busy) ? 0 : (uint32_t)(now - db->last_seen);
PDEBUG(DDB, DEBUG_NOTICE, "%d,%d,%05d\t%s\t\t%s\t\t%02d:%02d:%02d \t%d/%d\n", db->futln_nat, db->futln_fuvst, db->futln_rest, (db->eingebucht) ? "YES" : "-no-", (db->busy) ? "YES" : "-no-", last / 3600, (last / 60) % 60, last % 60, db->retry, MELDE_MAXIMAL);
sprintf(attached, "YES (OGK %d)", db->ogk_kanal);
PDEBUG(DDB, DEBUG_NOTICE, "%d,%d,%05d\t%s\t%s\t\t%02d:%02d:%02d \t%d/%s\n", db->futln_nat, db->futln_fuvst, db->futln_rest, (db->eingebucht) ? attached : "-no-\t", (db->busy) ? "YES" : "-no-", last / 3600, (last / 60) % 60, last % 60, db->retry, print_meldeaufrufe(si.meldeaufrufe));
db = db->next;
}
}

View File

@ -1,6 +1,6 @@
int update_db(cnetz_t *cnetz, uint8_t futln_nat, uint8_t futln_fuvst, uint16_t futln_rest, int *futelg_bit, int *extended, int busy, int failed);
int find_db(uint8_t futln_nat, uint8_t futln_fuvst, uint16_t futln_rest, int *futelg_bit, int *extended);
int update_db(uint8_t futln_nat, uint8_t futln_fuvst, uint16_t futln_rest, int ogk_kanal, int *futelg_bit, int *extended, int busy, int failed);
int find_db(uint8_t futln_nat, uint8_t futln_fuvst, uint16_t futln_rest, int *ogk_kanal, int *futelg_bit, int *extended);
void flush_db(void);
void dump_db(void);

View File

@ -568,10 +568,12 @@ static int fsk_distributed_encode(cnetz_t *cnetz, const char *bits)
/* decode samples and hut for bit changes
* use deviation to find greatest slope of the signal (bit change)
*/
void sender_receive(sender_t *sender, sample_t *samples, int length, double __attribute__((unused)) rf_level_db)
void sender_receive(sender_t *sender, sample_t *samples, int length, double rf_level_db)
{
cnetz_t *cnetz = (cnetz_t *) sender;
cnetz->rf_level_db = rf_level_db;
/* measure rx sample speed */
calc_clock_speed(cnetz, length, 0, 0);
@ -668,19 +670,11 @@ again:
switch (cnetz->dsp_mode) {
case DSP_MODE_OGK:
/* if automatic polarity selection is used, toggle between
* two polarities (every 4 slots) until a response is received
* then continue to use the time slots of that polarity
*/
if (cnetz->auto_polarity)
cnetz->negative_polarity = (cnetz->sched_ts & 7) >> 2;
/* send on timeslots depending on the polarity:
* positive polarity: ts, ts+8, ts+16, ts+24
* negative polarity: ts+4, ts+12, ts+20, ts+28
*/
if (((cnetz->sched_ts & 7) == (si.timeslot & 7) && cnetz->negative_polarity == 0)
|| ((cnetz->sched_ts & 7) == ((si.timeslot + 4) & 7) && cnetz->negative_polarity == 1)) {
if (((si.timeslots >> cnetz->sched_ts) & 1)) {
if (cnetz->sched_r_m == 0) {
/* if automatic polarity selection is used, toggle between two polarities (every transmitted slot) until a response is received then continue to use the time slots of that polarity */
if (cnetz->auto_polarity)
cnetz->negative_polarity = (cnetz->sched_ts & 7) >> 2;
/* set last time slot, so we know to which time slot the message from mobile station belongs to */
cnetz->sched_last_ts = cnetz->sched_ts;
bits = cnetz_encode_telegramm(cnetz);

View File

@ -44,19 +44,20 @@ 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;
const char *flip_polarity = "auto";
const char *flip_polarity = "";
int ms_power = 6; /* 1..8 */
int warteschlange = 1;
int challenge_valid;
uint64_t challenge;
int response_valid;
uint64_t response;
uint8_t timeslot = 0;
uint32_t timeslots = 4; /* 1 up to 8 */
uint8_t fuz_nat = 1;
uint8_t fuz_fuvst = 4;
uint8_t fuz_rest = 66;
const char *fuz_name = NULL;
uint8_t kennung_fufst = 1; /* normal prio */
uint8_t bahn_bs = 0; /* normal */
uint8_t authentifikationsbit = 0;
uint8_t ws_kennung = 0; /* no queue */
uint8_t fuvst_sperren = 0; /* no blocking registration/calls */
@ -72,6 +73,8 @@ 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 */
int meldeinterval = 120; /* when to ask the phone about beeing alive */
int meldeaufrufe = 3; /* how many times to ask phone about beeing alive */
enum demod_type demod = FSK_DEMOD_AUTO;
int metering = 20;
double speech_deviation = 4000.0; /* best results with all my equipment */
@ -82,6 +85,10 @@ void print_help(const char *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(" You must define at least one OgK at channel 131. This channel may be a\n");
printf(" a combined OgK+SpK channel, but this works with older phones only.\n");
printf(" You must define additionally one or more SpK, in order to make calls.\n");
printf(" You may define alternative OgK, the phones will attach to it then.\n");
printf(" -M --measure-speed\n");
printf(" Measures clock speed. THIS IS REQUIRED! See documentation!\n");
printf(" -C --clock-speed <rx ppm>,<tx ppm>\n");
@ -95,8 +102,8 @@ void print_help(const char *arg0)
printf(" generates a negative signal rather than a positive one. If auto, the\n");
printf(" base station uses double time slots with alternating polarity.\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(" (default = %s)\n", (flip_polarity[0]) ? flip_polarity : "auto");
printf(" Note: Correct polarity is selected for SDR by default.\n");
printf(" -P --ms-power <power level>\n");
printf(" Give power level of the mobile station: 1, 2, 4, 6, 8 (default = '%d')\n", ms_power);
printf(" 1 = 7.5-20 W; 2 = 4-8 W; 4 = 0.5-1 W; 6 = 50-125 mW; 8 = 2-10 mW\n");
@ -121,11 +128,10 @@ void print_help(const char *arg0)
printf(" It is unclear what the actual voice deviation is. Please decrease, if\n");
printf(" mobile's microphone is too loud and speaker is too quiet.\n");
printf(" (default = %.0f)\n", speech_deviation);
printf(" -S --sysinfo timeslot=<0..31>\n");
printf(" Set time slot of OgK broadcast. There are 32 time slots, but every 8th\n");
printf(" slot is used. This means if you select time slot 0, also slots 8, 16\n");
printf(" and 24 will be used. If you select slot 14, also slots 6, 22 and 30\n");
printf(" will be used. (default = %d)\n", timeslot);
printf(" -S --sysinfo timeslots=1|2|4|8\n");
printf(" Set number of timeslots of OgK broadcast. There are 32 time slots per\n");
printf(" frame, but only up to 8 slots can be used, because of processing\n");
printf(" delay. (default = %d)\n", timeslots);
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);
@ -145,6 +151,12 @@ void print_help(const char *arg0)
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 bahn-bs=<value>\n");
printf(" Set special tunnel base station mode for train mobile phones only.\n");
printf(" (default = %d)\n", kennung_fufst);
printf(" 0 = Disable (every phone is allowed)\n");
printf(" 1 = Enable (only train phones are allowed)\n");
printf(" Note: Enableing this will force priority to 0 (Test base station).\n");
printf(" -S --sysinfo auth=<auth>\n");
printf(" Enable authentication flag on the base station. Since we cannot\n");
printf(" authenticate, because we don't know the secret key and the algorithm,\n");
@ -217,6 +229,12 @@ void print_help(const char *arg0)
} else {
printf(" (default = %d-%d)\n", futln_sperre_start, futln_sperre_end);
}
printf(" -S --sysinfo meldeinterval=<seconds>\n");
printf(" Time to wait until pinging the phone wether it is still available.\n");
printf(" (default = %d)\n", meldeinterval);
printf(" -S --sysinfo meldeaufrufe=<count>\n");
printf(" Number of times we try to ping mobile until we assume it is gone.\n");
printf(" Use '0' for infinite tries. (default = %d)\n", meldeaufrufe);
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");
@ -346,8 +364,8 @@ static int handle_options(int short_option, int argi, char **argv)
return -EINVAL;
}
p++;
if (!strncasecmp(argv[argi], "timeslot=", p - argv[argi])) {
timeslot = atoi_limit(p, 0, 31);
if (!strncasecmp(argv[argi], "timeslots=", p - argv[argi])) {
timeslots = atoi_limit(p, 1, 8);
} else
if (!strncasecmp(argv[argi], "fuz-nat=", p - argv[argi])) {
fuz_nat = atoi_limit(p, 0, 7);
@ -380,6 +398,9 @@ error_fuz:
if (!strncasecmp(argv[argi], "kennung-fufst=", p - argv[argi])) {
kennung_fufst = atoi_limit(p, 0, 3);
} else
if (!strncasecmp(argv[argi], "bahn-bs=", p - argv[argi])) {
bahn_bs = atoi_limit(p, 0, 1);
} else
if (!strncasecmp(argv[argi], "auth=", p - argv[argi])) {
authentifikationsbit = atoi_limit(p, 0, 1);
} else
@ -433,6 +454,12 @@ error_fuz:
futln_sperre_end = atoi(q) & 0xf;
}
} else
if (!strncasecmp(argv[argi], "meldeinterval=", p - argv[argi])) {
meldeinterval = atoi_limit(p, 1, 20 * 60);
} else
if (!strncasecmp(argv[argi], "meldeaufrufe=", p - argv[argi])) {
meldeaufrufe = atoi_limit(p, 0, 1000000);
} else
{
fprintf(stderr, "Given sysinfo parameter '%s' unknown, use '-h' for help!\n", argv[argi]);
return -EINVAL;
@ -524,7 +551,7 @@ int main(int argc, char *argv[])
}
if (!num_kanal) {
printf("No channel (\"Kanal\") is specified, I suggest channel %d.\n\n", CNETZ_OGK_KANAL);
printf("No channel (\"Kanal\") is specified, I suggest channel %d.\n\n", CNETZ_STD_OGK_KANAL);
mandatory = 1;
}
if (use_sdr) {
@ -575,7 +602,13 @@ int main(int argc, char *argv[])
}
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(timeslot, fuz_nat, fuz_fuvst, fuz_rest, kennung_fufst, authentifikationsbit, ws_kennung, fuvst_sperren, grenz_einbuchen, grenz_umschalten, grenz_ausloesen, mittel_umschalten, mittel_ausloesen, genauigkeit, bewertung, entfernung, reduzierung, nachbar_prio, teilnehmergruppensperre, anzahl_gesperrter_teilnehmergruppen);
switch(timeslots) {
case 1: timeslots=0x00000001; break;
case 2: timeslots=0x00010001; break;
case 4: timeslots=0x01010101; break;
default: timeslots=0x11111111;
}
init_sysinfo(timeslots, fuz_nat, fuz_fuvst, fuz_rest, kennung_fufst, bahn_bs, authentifikationsbit, ws_kennung, fuvst_sperren, grenz_einbuchen, grenz_umschalten, grenz_ausloesen, mittel_umschalten, mittel_ausloesen, genauigkeit, bewertung, entfernung, reduzierung, nachbar_prio, teilnehmergruppensperre, anzahl_gesperrter_teilnehmergruppen, meldeinterval, meldeaufrufe);
dsp_init();
rc = init_telegramm();
if (rc < 0) {
@ -585,7 +618,7 @@ int main(int argc, char *argv[])
init_coding();
cnetz_init();
/* check for mandatory OgK */
/* check for mandatory standard OgK */
for (i = 0; i < num_kanal; i++) {
if (chan_type[i] == CHAN_TYPE_OGK || chan_type[i] == CHAN_TYPE_OGK_SPK)
break;
@ -629,7 +662,7 @@ int main(int argc, char *argv[])
polarity = 1; /* positive */
if (!strcmp(flip_polarity, "yes"))
polarity = -1; /* negative */
if (use_sdr && polarity == 0)
if (use_sdr && !flip_polarity[0])
polarity = 1; /* SDR is always positive */
/* demodulation algorithm */

View File

@ -7,10 +7,10 @@
/* The list of cell towers is ripped from BSA61 phone's firmware */
static struct cnetz_stations {
char standort[32], name[8];
char long_name[32], name[8];
uint8_t nat, fuvst, rest;
} cnetz_stations[] = {
/* Standort-Name Kurzer Name, Nat FuVST Rest */
/* Langer-Name Kurzer Name, Nat FuVST Rest */
{ "Oberhausen 32", "OB32", 1, 2, 1, },
{ "Oberhausen 0 <prov.>", "OB0", 1, 2, 5, },
{ "Wuppertal 18", "WUP18", 1, 2, 6, },
@ -2143,37 +2143,37 @@ void station_list(void)
char name[33];
printf("List of all base stations:\n\n");
printf("Name Standort Nat FuVst Rest\n");
printf("Kurz Name Nat FuVst Rest\n");
printf("------------------------------------------------------------\n");
for (i = 0; cnetz_stations[i].standort[0]; i++) {
for (i = 0; cnetz_stations[i].long_name[0]; i++) {
memset(name, ' ', sizeof(name));
memcpy(name, cnetz_stations[i].name, strlen(cnetz_stations[i].name));
name[8] = '\0';
printf("%s", name);
memset(name, ' ', sizeof(name));
memcpy(name, cnetz_stations[i].standort, strlen(cnetz_stations[i].standort));
memcpy(name, cnetz_stations[i].long_name, strlen(cnetz_stations[i].long_name));
name[sizeof(name) - 1] = '\0';
printf("%s%d\t%d\t%d\n", name, cnetz_stations[i].nat, cnetz_stations[i].fuvst, cnetz_stations[i].rest);
}
}
const char *get_station_name(uint8_t nat, uint8_t fuvst, uint8_t rest, const char **standort)
const char *get_station_name(uint8_t nat, uint8_t fuvst, uint8_t rest, const char **long_name)
{
int i;
for (i = 0; cnetz_stations[i].standort[0]; i++) {
for (i = 0; cnetz_stations[i].long_name[0]; i++) {
if (cnetz_stations[i].nat == nat
&& cnetz_stations[i].fuvst == fuvst
&& cnetz_stations[i].rest == rest) {
if (standort)
*standort = cnetz_stations[i].standort;
if (long_name)
*long_name = cnetz_stations[i].long_name;
return cnetz_stations[i].name;
}
}
if (standort)
*standort = "unknown";
return *standort;
if (long_name)
*long_name = "unknown";
return *long_name;
}
const char *get_station_id(const char *name, uint8_t *nat, uint8_t *fuvst, uint8_t *rest)
@ -2182,7 +2182,8 @@ const char *get_station_id(const char *name, uint8_t *nat, uint8_t *fuvst, uint8
for (i = 0; cnetz_stations[i].name[0]; i++) {
/* check for given prefix */
if (!strncasecmp(cnetz_stations[i].name, name, strlen(name))) {
if (!strncasecmp(cnetz_stations[i].name, name, strlen(name))
|| !strncasecmp(cnetz_stations[i].long_name, name, strlen(name)) ) {
/* found twice */
if (found >= 0)
return "Given station name is ambiguous, use more letters! Use '-S fuz-name=list' to get a list of all stations.";

View File

@ -4,12 +4,12 @@
cnetz_si si;
void init_sysinfo(uint8_t timeslot, uint8_t fuz_nat, uint8_t fuz_fuvst, uint8_t fuz_rest, uint8_t kennung_fufst, uint8_t authentifikationsbit, 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)
void init_sysinfo(uint32_t timeslots, uint8_t fuz_nat, uint8_t fuz_fuvst, uint8_t fuz_rest, uint8_t kennung_fufst, uint8_t bahn_bs, uint8_t authentifikationsbit, 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, int meldeinterval, int meldeaufrufe)
{
memset(&si, 0, sizeof(si));
/* timeslot to use */
si.timeslot = timeslot;
si.timeslots = timeslots;
/* ID of base station */
si.fuz_nat = fuz_nat;
@ -37,7 +37,10 @@ void init_sysinfo(uint8_t timeslot, uint8_t fuz_nat, uint8_t fuz_fuvst, uint8_t
/* a low value is tollerant to bad quality */
si.grenz_einbuchen = grenz_einbuchen; /* 1..7 */
if (bahn_bs)
kennung_fufst = 0;
si.kennung_fufst = kennung_fufst;
si.bahn_bs = bahn_bs;
si.authentifikationsbit = authentifikationsbit;
@ -52,5 +55,8 @@ void init_sysinfo(uint8_t timeslot, uint8_t fuz_nat, uint8_t fuz_fuvst, uint8_t
/* deny group of subscribers. (used to balance subscribers between base stations) */
si.teilnehmergruppensperre = teilnehmergruppensperre;
si.anzahl_gesperrter_teilnehmergruppen = anzahl_gesperrter_teilnehmergruppen;
si.meldeinterval = meldeinterval;
si.meldeaufrufe = meldeaufrufe;
}

View File

@ -1,6 +1,6 @@
typedef struct system_information {
uint8_t timeslot; /* timeslot to use */
uint32_t timeslots; /* timeslot map to use */
uint8_t fuz_nat; /* national network ID */
uint8_t fuz_fuvst; /* id of switching center */
uint8_t fuz_rest; /* rest of base station id */
@ -13,6 +13,7 @@ typedef struct system_information {
uint8_t entfernung;
uint8_t grenz_einbuchen;
uint8_t kennung_fufst; /* prio of base station */
uint8_t bahn_bs; /* special train base station */
uint8_t authentifikationsbit; /* base station suppoerts authentication */
uint8_t ws_kennung; /* queue setting sof base station */
uint8_t nachbar_prio;
@ -20,9 +21,11 @@ typedef struct system_information {
uint8_t reduzierung;
int8_t teilnehmergruppensperre;
int8_t anzahl_gesperrter_teilnehmergruppen;
int meldeinterval; /* when to retry availability check */
int meldeaufrufe; /* 0 for infinite */
} cnetz_si;
extern cnetz_si si;
void init_sysinfo(uint8_t timeslot, uint8_t fuz_nat, uint8_t fuz_fuvst, uint8_t fuz_rest, uint8_t kennung_fufst, uint8_t authentifikationsbit, 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);
void init_sysinfo(uint32_t timeslots, uint8_t fuz_nat, uint8_t fuz_fuvst, uint8_t fuz_rest, uint8_t kennung_fufst, uint8_t bahn_bs, uint8_t authentifikationsbit, 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, int meldeinterval, int meldeaufrufe);

View File

@ -632,7 +632,8 @@ static char *assemble_telegramm(const telegramm_t *telegramm, int debug)
abort();
}
PDEBUG(DFRAME, DEBUG_DEBUG, "Coding %s %s\n", definition_opcode[telegramm->opcode].message_name, definition_opcode[telegramm->opcode].message_text);
if (debug)
PDEBUG(DFRAME, DEBUG_INFO, "Coding %s %s\n", definition_opcode[telegramm->opcode].message_name, definition_opcode[telegramm->opcode].message_text);
/* copy opcode */
for (i = 0; i < 6; i++)
@ -847,7 +848,7 @@ static void disassemble_telegramm(telegramm_t *telegramm, const char *bits, int
value = (value << 1) | (bits[i] == '1');
telegramm->opcode = value;
PDEBUG(DFRAME, DEBUG_DEBUG, "Decoding %s %s\n", definition_opcode[telegramm->opcode].message_name, definition_opcode[telegramm->opcode].message_text);
PDEBUG(DFRAME, DEBUG_INFO, "Decoding %s %s\n", definition_opcode[telegramm->opcode].message_name, definition_opcode[telegramm->opcode].message_text);
/* copy parameters */
if (auth && bits[1]) /* auth flag and chip card flag */
@ -1499,10 +1500,9 @@ void cnetz_decode_telegramm(cnetz_t *cnetz, const char *bits, double level, doub
return;
}
PDEBUG_CHAN(DDSP, DEBUG_INFO, "RF level: %.1f dB RX Level: %.0f%% Standard deviation: %.0f%% Sync Time: %.2f (TS %.2f) %s\n", cnetz->rf_level_db, fabs(level) / cnetz->fsk_deviation * 100.0, stddev / fabs(level) * 100.0, sync_time, sync_time / 396.0, (level < 0) ? "NEGATIVE (phone's mode)" : "POSITIVE (base station's mode)");
if (bit_errors)
PDEBUG_CHAN(DDSP, DEBUG_INFO, "RX Level: %.0f%% Standard deviation: %.0f%% Sync Time: %.2f (TS %.2f) Bit errors: %d %s\n", fabs(level) / cnetz->fsk_deviation * 100.0, stddev / fabs(level) * 100.0, sync_time, sync_time / 396.0, bit_errors, (level < 0) ? "NEGATIVE (phone's mode)" : "POSITIVE (base station's mode)");
else
PDEBUG_CHAN(DDSP, DEBUG_INFO, "RX Level: %.0f%% Standard deviation: %.0f%% Sync Time: %.2f (TS %.2f) %s\n", fabs(level) / cnetz->fsk_deviation * 100.0, stddev / fabs(level) * 100.0, sync_time, sync_time / 396.0, (level < 0) ? "NEGATIVE (phone's mode)" : "POSITIVE (base station's mode)");
PDEBUG_CHAN(DDSP, DEBUG_INFO, " -> Frame has %d bit errors.\n", bit_errors);
disassemble_telegramm(&telegramm, bits, si.authentifikationsbit);
opcode = telegramm.opcode;
@ -1579,6 +1579,7 @@ const char *cnetz_encode_telegramm(cnetz_t *cnetz)
const telegramm_t *telegramm = NULL;
uint8_t opcode;
char *bits;
int debug = 1;
switch (cnetz->dsp_mode) {
case DSP_MODE_OGK:
@ -1601,7 +1602,11 @@ const char *cnetz_encode_telegramm(cnetz_t *cnetz)
return NULL;
opcode = telegramm->opcode;
bits = assemble_telegramm(telegramm, (opcode != OPCODE_LR_R) && (opcode != OPCODE_MLR_M));
if (opcode == OPCODE_LR_R && cnetz->sched_lr_debugged)
debug = 0;
if (opcode == OPCODE_MLR_M && cnetz->sched_mlr_debugged)
debug = 0;
bits = assemble_telegramm(telegramm, debug);
bits = encode(bits);
bits = interleave(bits);
@ -1613,6 +1618,13 @@ const char *cnetz_encode_telegramm(cnetz_t *cnetz)
bits[i] ^= 1;
}
if (opcode == OPCODE_LR_R && !cnetz->sched_lr_debugged)
cnetz->sched_lr_debugged = 1;
if (opcode == OPCODE_MLR_M && !cnetz->sched_mlr_debugged) {
cnetz->sched_mlr_debugged = 1;
PDEBUG(DFRAME, DEBUG_INFO, "Subsequent IDLE frames are not show, to prevent flooding the output.\n");
}
return bits;
}

View File

@ -20,6 +20,7 @@
#include <stdio.h>
#include <stdint.h>
#include <stdlib.h>
#include <math.h>
#include "../libsample/sample.h"
#include "../libdebug/debug.h"
#include "../libmobile/call.h"
@ -28,6 +29,8 @@
#include "telegramm.h"
#include "database.h"
static int new_cueue_position = 0;
const char *transaction2rufnummer(transaction_t *trans)
{
static char rufnummer[32]; /* make GCC happy (overflow check) */
@ -38,24 +41,34 @@ const char *transaction2rufnummer(transaction_t *trans)
}
/* create transaction */
transaction_t *create_transaction(cnetz_t *cnetz, uint64_t state, uint8_t futln_nat, uint8_t futln_fuvst, uint16_t futln_rest, int futelg_bit, 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 futelg_bit, int extended, double rf_level_db)
{
sender_t *sender;
transaction_t *trans = NULL;
cnetz_t *search_cnetz;
transaction_t *trans;
/* search transaction for this subscriber */
for (sender = sender_head; sender; sender = sender->next) {
search_cnetz = (cnetz_t *) sender;
/* search transaction for this callref */
trans = search_transaction_number(search_cnetz, futln_nat, futln_fuvst, futln_rest);
if (trans)
break;
}
trans = search_transaction_number_global(futln_nat, futln_fuvst, futln_rest);
if (trans) {
const char *rufnummer = transaction2rufnummer(trans);
int old_callref = trans->callref;
cnetz_t *old_cnetz = trans->cnetz;
/* both states must be the same and one of the give selection */
if ((trans->state & state & (TRANS_EM | TRANS_UM | TRANS_VWG | TRANS_ATQ_IDLE))) {
if (!isnan(trans->rf_level_db) && !isnan(rf_level_db) && trans->cnetz->kanal != cnetz->kanal) {
if (rf_level_db > trans->rf_level_db) {
PDEBUG(DTRANS, DEBUG_NOTICE, "Found already pending transaction for subscriber '%s' on channel #%d, but this message on channel #%d is stronger, so we move to that channel!\n", rufnummer, trans->cnetz->kanal, cnetz->kanal);
trans->rf_level_db = rf_level_db;
unlink_transaction(trans);
link_transaction(trans, cnetz);
update_db(trans->futln_nat, trans->futln_fuvst, trans->futln_rest, cnetz->kanal, NULL, NULL, 1, 0);
return trans;
}
if (rf_level_db < trans->rf_level_db) {
PDEBUG(DTRANS, DEBUG_NOTICE, "Found already pending transaction for subscriber '%s' on channel #%d, but this message on channel #%d is weaker, so we ignore that channel!\n", rufnummer, trans->cnetz->kanal, cnetz->kanal);
return trans;
}
}
PDEBUG(DTRANS, DEBUG_NOTICE, "Found already pending transaction for subscriber '%s' on channel #%d, but this message on channel #%d is also received. Try to avoid multiple OgK channels!\n", rufnummer, trans->cnetz->kanal, cnetz->kanal);
return trans;
}
PDEBUG(DTRANS, DEBUG_NOTICE, "Found already pending transaction for subscriber '%s', deleting!\n", rufnummer);
destroy_transaction(trans);
if (old_cnetz) /* should be... */
@ -88,10 +101,12 @@ transaction_t *create_transaction(cnetz_t *cnetz, uint64_t state, uint8_t futln_
link_transaction(trans, cnetz);
/* update database: now busy */
update_db(cnetz, futln_nat, futln_fuvst, futln_rest, &futelg_bit, &extended, 1, 0);
update_db(futln_nat, futln_fuvst, futln_rest, cnetz->kanal, &futelg_bit, &extended, 1, 0);
trans->futelg_bit = futelg_bit;
trans->extended = extended;
trans->rf_level_db = rf_level_db;
return trans;
}
@ -99,7 +114,7 @@ transaction_t *create_transaction(cnetz_t *cnetz, uint64_t state, uint8_t futln_
void destroy_transaction(transaction_t *trans)
{
/* update database: now idle */
update_db(trans->cnetz, trans->futln_nat, trans->futln_fuvst, trans->futln_rest, NULL, NULL, 0, trans->page_failed);
update_db(trans->futln_nat, trans->futln_fuvst, trans->futln_rest, 0, NULL, NULL, 0, trans->page_failed);
unlink_transaction(trans);
@ -182,6 +197,23 @@ transaction_t *search_transaction_number(cnetz_t *cnetz, uint8_t futln_nat, uint
return NULL;
}
transaction_t *search_transaction_number_global(uint8_t futln_nat, uint8_t futln_fuvst, uint16_t futln_rest)
{
sender_t *sender;
cnetz_t *cnetz;
transaction_t *trans = NULL;
/* search transaction for this subscriber */
for (sender = sender_head; sender; sender = sender->next) {
cnetz = (cnetz_t *) sender;
/* search transaction for this callref */
trans = search_transaction_number(cnetz, futln_nat, futln_fuvst, futln_rest);
if (trans)
break;
}
return trans;
}
transaction_t *search_transaction_callref(cnetz_t *cnetz, int callref)
{
transaction_t *trans = cnetz->trans_list;
@ -201,6 +233,42 @@ transaction_t *search_transaction_callref(cnetz_t *cnetz, int callref)
return NULL;
}
/* get oldest transaction in queue:
*
* oldest means that the queue number is the smallest.
* all candidates (transactions) must be in queue state.
*/
transaction_t *search_transaction_queue(void)
{
sender_t *sender;
transaction_t *trans, *found = NULL;
cnetz_t *cnetz;
int queue_max = 0;
for (sender = sender_head; sender; sender = sender->next) {
cnetz = (cnetz_t *) sender;
trans = cnetz->trans_list;
while (trans) {
if ((trans->state & (TRANS_MO_QUEUE | TRANS_MT_QUEUE))) {
/* select if first or lower number */
if (!found || trans->queue_position < queue_max) {
queue_max = trans->queue_position;
found = trans;
}
}
trans = trans->next;
}
}
if (found) {
const char *rufnummer = transaction2rufnummer(found);
PDEBUG(DTRANS, DEBUG_DEBUG, "Found oldest transaction in queue for subscriber '%s'\n", rufnummer);
return found;
}
return NULL;
}
static const char *trans_state_name(uint64_t state)
{
switch (state) {
@ -252,6 +320,8 @@ static const char *trans_state_name(uint64_t state)
return "AT";
case TRANS_ATQ:
return "ATQ";
case TRANS_ATQ_IDLE:
return "ATQ_IDLE";
case TRANS_MO_QUEUE:
return "MO_QUEUE";
case TRANS_MT_QUEUE:
@ -301,6 +371,7 @@ const char *trans_short_state_name(uint64_t state)
case TRANS_AF:
case TRANS_AT:
case TRANS_ATQ:
case TRANS_ATQ_IDLE:
return "RELEASE";
case TRANS_MO_QUEUE:
case TRANS_MO_DELAY:
@ -317,6 +388,9 @@ void trans_new_state(transaction_t *trans, uint64_t 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;
/* in case of a queue, set new positon */
if (!trans->queue_position && (state == TRANS_MO_QUEUE || state == TRANS_MT_QUEUE))
trans->queue_position = ++new_cueue_position;
cnetz_display_status();
}

View File

@ -29,11 +29,12 @@
#define TRANS_AF (1 << 20) /* release connection by base station (SpK) */
#define TRANS_AT (1 << 21) /* release connection by mobile station */
#define TRANS_ATQ (1 << 22) /* acknowledge release of MO call in queue */
#define TRANS_ATQ_IDLE (1 << 23) /* repeat, if call has been released already (mobile sends again) */
/* queue */
#define TRANS_MO_QUEUE (1 << 23) /* MO queue */
#define TRANS_MT_QUEUE (1 << 24) /* MT queue */
#define TRANS_MO_DELAY (1 << 25) /* delay to be sure the channel is free again */
#define TRANS_MT_DELAY (1 << 26)
#define TRANS_MO_QUEUE (1 << 24) /* MO queue */
#define TRANS_MT_QUEUE (1 << 25) /* MT queue */
#define TRANS_MO_DELAY (1 << 26) /* delay to be sure the channel is free again */
#define TRANS_MT_DELAY (1 << 27)
typedef struct transaction {
struct transaction *next; /* pointer to next node in list */
@ -54,16 +55,20 @@ typedef struct transaction {
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) */
int queue_position; /* to find next transaction in queue */
double rf_level_db; /* level of first contact, so we can detect correct channel at multiple receptions */
} transaction_t;
const char *transaction2rufnummer(transaction_t *trans);
transaction_t *create_transaction(cnetz_t *cnetz, uint64_t state, uint8_t futln_nat, uint8_t futln_fuvst, uint16_t futln_rest, int futelg_bit, 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 futelg_bit, int extended, double rf_level_db);
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, 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_number_global(uint8_t futln_nat, uint8_t futln_fuvst, uint16_t futln_rest);
transaction_t *search_transaction_callref(cnetz_t *cnetz, int callref);
transaction_t *search_transaction_queue(void);
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);

23
src/dcf77/Makefile.am Normal file
View File

@ -0,0 +1,23 @@
AM_CPPFLAGS = -Wall -Wextra -g $(all_includes)
if HAVE_ALSA
bin_PROGRAMS = \
dcf77
dcf77_SOURCES = \
dcf77.c \
main.c
dcf77_LDADD = \
$(COMMON_LA) \
$(top_builddir)/src/liboptions/liboptions.a \
$(top_builddir)/src/libdebug/libdebug.a \
$(top_builddir)/src/libdisplay/libdisplay.a \
$(top_builddir)/src/libfilter/libfilter.a \
$(top_builddir)/src/libwave/libwave.a \
$(top_builddir)/src/libsample/libsample.a \
$(top_builddir)/src/libsound/libsound.a \
$(top_builddir)/src/libaaimage/libaaimage.a \
$(ALSA_LIBS) \
-lm
endif

585
src/dcf77/dcf77.c Normal file
View File

@ -0,0 +1,585 @@
/* implementation of DCF77 transmitter and receiver
*
* (C) 2022 by Andreas Eversberg <jolly@eversberg.eu>
* All Rights Reserved
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#include <stdio.h>
#include <string.h>
#include <unistd.h>
#include <errno.h>
#include <stdint.h>
#include <inttypes.h>
#include <stdlib.h>
#include <math.h>
#include "../libdebug/debug.h"
#include "dcf77.h"
double get_time(void);
#define CARRIER_FREQUENCY 77500
#define TEST_FREQUENCY 1000
#define CARRIER_BANDWIDTH 10.0
#define SAMPLE_CLOCK 1000
#define CLOCK_1S 1.0
#define CLOCK_BANDWIDTH 0.1
#define REDUCTION_FACTOR 0.15
#define REDUCTION_TH 0.575
#define TX_LEVEL 0.9
#define level2db(level) (20 * log10(level))
static int fast_math = 0;
static float *sin_tab = NULL, *cos_tab = NULL;
const char *time_zone[4] = { "???", "CEST", "CET", "???" };
const char *week_day[8] = { "???", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat", "Sun" };
const char *month_name[13] = { "???", "Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec" };
/* global init */
int dcf77_init(int _fast_math)
{
fast_math = _fast_math;
if (fast_math) {
int i;
sin_tab = calloc(65536+16384, sizeof(*sin_tab));
if (!sin_tab) {
fprintf(stderr, "No mem!\n");
return -ENOMEM;
}
cos_tab = sin_tab + 16384;
/* generate sine and cosine */
for (i = 0; i < 65536+16384; i++)
sin_tab[i] = sin(2.0 * M_PI * (double)i / 65536.0);
}
return 0;
}
/* global exit */
void dcf77_exit(void)
{
if (sin_tab) {
free(sin_tab);
sin_tab = cos_tab = NULL;
}
}
dcf77_t *dcf77_create(int samplerate, int use_tx, int use_rx, int test_tone)
{
dcf77_t *dcf77 = NULL;
dcf77_tx_t *tx;
dcf77_rx_t *rx;
dcf77 = calloc(1, sizeof(*dcf77));
if (!dcf77) {
PDEBUG(DDCF77, DEBUG_ERROR, "No mem!\n");
return NULL;
}
tx = &dcf77->tx;
rx = &dcf77->rx;
/* measurement */
display_wave_init(&dcf77->dispwav, (double)samplerate, "DCF77");
display_measurements_init(&dcf77->dispmeas, samplerate, "DCF77");
/* prepare tx */
if (use_tx) {
tx->enable = 1;
if (fast_math)
tx->phase_360 = 65536.0;
else
tx->phase_360 = 2.0 * M_PI;
/* carrier generation */
tx->carrier_phase_step = tx->phase_360 * (double)CARRIER_FREQUENCY / ((double)samplerate);
tx->test_phase_step = tx->phase_360 * (double)TEST_FREQUENCY / ((double)samplerate);
tx->waves_0 = CARRIER_FREQUENCY / 10;
tx->waves_1 = CARRIER_FREQUENCY / 5;
tx->waves_sec = CARRIER_FREQUENCY;
tx->test_tone = test_tone;
}
/* prepare rx */
if (use_rx) {
rx->enable = 1;
if (fast_math)
rx->phase_360 = 65536.0;
else
rx->phase_360 = 2.0 * M_PI;
/* carrier filter */
rx->carrier_phase_step = rx->phase_360 * (double)CARRIER_FREQUENCY / ((double)samplerate);
/* use fourth order (2 iter) filter, since it is as fast as second order (1 iter) filter */
iir_lowpass_init(&rx->carrier_lp[0], CARRIER_BANDWIDTH, (double)samplerate, 2);
iir_lowpass_init(&rx->carrier_lp[1], CARRIER_BANDWIDTH, (double)samplerate, 2);
/* signal rate */
rx->sample_step = (double)SAMPLE_CLOCK / (double)samplerate;
/* delay buffer */
rx->delay_size = ceil((double)SAMPLE_CLOCK * 0.1);
rx->delay_buffer = calloc(rx->delay_size, sizeof(*rx->delay_buffer));
if (!rx->delay_buffer) {
PDEBUG(DDCF77, DEBUG_ERROR, "No mem!\n");
return NULL;
}
/* count clock signal */
rx->clock_count = -1;
/* measurement parameters */
dcf77->dmp_input_level = display_measurements_add(&dcf77->dispmeas, "Input Level", "%.0f dB", DISPLAY_MEAS_AVG, DISPLAY_MEAS_LEFT, -100.0, 0.0, -INFINITY);
dcf77->dmp_signal_level = display_measurements_add(&dcf77->dispmeas, "Signal Level", "%.0f dB", DISPLAY_MEAS_AVG, DISPLAY_MEAS_LEFT, -100.0, 0.0, -INFINITY);
dcf77->dmp_signal_quality = display_measurements_add(&dcf77->dispmeas, "Signal Qualtiy", "%.0f %%", DISPLAY_MEAS_LAST, DISPLAY_MEAS_LEFT, 0.0, 100.0, -INFINITY);
}
if (tx->enable)
PDEBUG(DDCF77, DEBUG_INFO, "DCF77 transmitter has been created.\n");
if (rx->enable)
PDEBUG(DDCF77, DEBUG_INFO, "DCF77 receiver has been created.\n");
return dcf77;
}
void dcf77_destroy(dcf77_t *dcf77)
{
if (dcf77) {
dcf77_rx_t *rx = &dcf77->rx;
free(rx->delay_buffer);
free(dcf77);
}
PDEBUG(DDCF77, DEBUG_INFO, "DCF77 has been destroyed.\n");
}
/* set inital time stamp at the moment the stream starts */
void dcf77_tx_start(dcf77_t *dcf77, time_t timestamp)
{
dcf77_tx_t *tx = &dcf77->tx;
double now;
time_t t;
/* get time stamp */
if (timestamp < 0)
now = get_time();
else
now = timestamp;
t = floor(now);
/* current second within minute */
tx->second = t % 60;
/* time stamp of next minute */
tx->timestamp = t - tx->second + 60;
/* wave within current second */
tx->wave = floor(fmod(now, 1.0) * (double)tx->waves_sec);
/* silence until next second begins */
tx->symbol = 'm'; tx->level = 0;
}
static char tx_symbol(dcf77_t *dcf77, time_t timestamp, int second)
{
dcf77_tx_t *tx = &dcf77->tx;
char symbol;
/* generate frame */
if (second == 0 || !tx->data_frame) {
struct tm *tm;
int isdst_next_hour, wday, zone;
uint64_t frame = 0, p;
timestamp += 3600;
tm = localtime(&timestamp);
timestamp -= 3600;
if (!tm) {
error_tm:
PDEBUG(DDCF77, DEBUG_ERROR, "Failed to get local time of time stamp!\n");
return 'm';
}
isdst_next_hour = tm->tm_isdst;
tm = localtime(&timestamp);
if (!tm)
goto error_tm;
if (tm->tm_wday > 0)
wday = tm->tm_wday;
else
wday = 7;
if (tm->tm_isdst > 0)
zone = 1;
else
zone = 2;
PDEBUG(DDCF77, DEBUG_NOTICE, "The time transmitting: %s %s %d %02d:%02d:00 %s %02d\n", week_day[wday], month_name[tm->tm_mon + 1], tm->tm_mday, tm->tm_hour, tm->tm_min, time_zone[zone], tm->tm_year + 1900);
if ((tm->tm_isdst > 0) != (isdst_next_hour > 0))
frame |= (uint64_t)1 << 16;
if (tm->tm_isdst > 0)
frame |= (uint64_t)1 << 17;
else
frame |= (uint64_t)2 << 17;
frame |= 1 << 20;
frame |= (uint64_t)(tm->tm_min % 10) << 21;
frame |= (uint64_t)(tm->tm_min / 10) << 25;
p = (frame >> 21) & 0x7f;
p = p ^ (p >> 4);
p = p ^ (p >> 2);
p = p ^ (p >> 1);
frame |= (uint64_t)(p & 1) << 28;
frame |= (uint64_t)(tm->tm_hour % 10) << 29;
frame |= (uint64_t)(tm->tm_hour / 10) << 33;
p = (frame >> 29) & 0x3f;
p = p ^ (p >> 4);
p = p ^ (p >> 2);
p = p ^ (p >> 1);
frame |= (uint64_t)(p & 1) << 35;
frame |= (uint64_t)(tm->tm_mday % 10) << 36;
frame |= (uint64_t)(tm->tm_mday / 10) << 40;
frame |= (uint64_t)(wday) << 42;
frame |= (uint64_t)((tm->tm_mon + 1) % 10) << 45;
frame |= (uint64_t)((tm->tm_mon + 1) / 10) << 49;
frame |= (uint64_t)(tm->tm_year % 10) << 50;
frame |= (uint64_t)((tm->tm_year / 10) % 10) << 54;
p = (frame >> 36) & 0x3fffff;
p = p ^ (p >> 16);
p = p ^ (p >> 8);
p = p ^ (p >> 4);
p = p ^ (p >> 2);
p = p ^ (p >> 1);
frame |= (uint64_t)(p & 1) << 58;
tx->data_frame = frame;
}
if (second == 59)
symbol = 'm';
else symbol = ((tx->data_frame >> second) & 1) + '0';
PDEBUG(DDSP, DEBUG_DEBUG, "Trasmitting symbol '%c' (Bit %d)\n", symbol, second);
return symbol;
}
void dcf77_encode(dcf77_t *dcf77, sample_t *samples, int length)
{
dcf77_tx_t *tx = &dcf77->tx;
double carrier_phase, test_phase;
int i;
if (!tx->enable) {
memset(samples, 0, sizeof(*samples) * length);
return;
}
carrier_phase = tx->carrier_phase;
test_phase = tx->test_phase;
for (i = 0; i < length; i++) {
if (fast_math)
samples[i] = sin_tab[(uint16_t)carrier_phase] * tx->level;
else
samples[i] = sin(carrier_phase) * tx->level;
carrier_phase += tx->carrier_phase_step;
if (carrier_phase >= tx->phase_360) {
carrier_phase -= tx->phase_360;
tx->wave++;
if (tx->wave >= tx->waves_sec) {
tx->wave -= tx->waves_sec;
if (++tx->second == 60) {
tx->second = 0;
tx->timestamp += 60;
}
tx->symbol = tx_symbol(dcf77, tx->timestamp, tx->second);
}
switch (tx->symbol) {
case '0':
if (tx->wave < tx->waves_0)
tx->level = TX_LEVEL * REDUCTION_FACTOR;
else
tx->level = TX_LEVEL;
break;
case '1':
if (tx->wave < tx->waves_1)
tx->level = TX_LEVEL * REDUCTION_FACTOR;
else
tx->level = TX_LEVEL;
break;
case 'm':
tx->level = TX_LEVEL;
break;
}
if (tx->test_tone)
tx->level *= 0.9; /* 90 % */
}
if (tx->test_tone) {
if (fast_math)
samples[i] += sin_tab[(uint16_t)test_phase] * tx->level / 10.0; /* 10 % */
else
samples[i] += sin(test_phase) * tx->level / 10.0; /* 10 % */
if (test_phase >= tx->phase_360)
test_phase -= tx->phase_360;
test_phase += tx->test_phase_step;
}
}
tx->carrier_phase = carrier_phase;
tx->test_phase = test_phase;
}
static void rx_frame(uint64_t frame)
{
int zone;
int minute_one, minute_ten, minute = -1;
int hour_one, hour_ten, hour = -1;
int day_one, day_ten, day = -1;
int wday = -1;
int month_one, month_ten, month = -1;
int year_one, year_ten, year = -1;
uint64_t p;
PDEBUG(DFRAME, DEBUG_INFO, "Bit 0 is '0'? : %s\n", ((frame >> 0) & 1) ? "no" : "yes");
PDEBUG(DFRAME, DEBUG_INFO, "Bits 1..14 : 0x%04x\n", (int)(frame >> 1) & 0x3fff);
PDEBUG(DFRAME, DEBUG_INFO, "Call Bit : %d\n", (int)(frame >> 15) & 1);
PDEBUG(DFRAME, DEBUG_INFO, "Change Time Zone : %s\n", ((frame >> 16) & 1) ? "yes" : "no");
zone = ((frame >> 17) & 3);
PDEBUG(DFRAME, DEBUG_INFO, "Time Zone : %s\n", time_zone[zone]);
PDEBUG(DFRAME, DEBUG_INFO, "Add Leap Second : %s\n", ((frame >> 19) & 1) ? "yes" : "no");
PDEBUG(DFRAME, DEBUG_INFO, "Bit 20 is '1'? : %s\n", ((frame >> 20) & 1) ? "yes" : "no");
minute_one = (frame >> 21 & 0xf);
minute_ten = ((frame >> 25) & 0x7);
p = (frame >> 21) & 0xff;
p = p ^ (p >> 4);
p = p ^ (p >> 2);
p = p ^ (p >> 1);
if (minute_one > 9 || minute_ten > 5 || (p & 1))
PDEBUG(DFRAME, DEBUG_INFO, "Minute : ???\n");
else {
minute = minute_ten * 10 + minute_one;
PDEBUG(DFRAME, DEBUG_INFO, "Minute : %02d\n", minute);
}
hour_one = (frame >> 29 & 0xf);
hour_ten = ((frame >> 33) & 0x3);
p = (frame >> 29) & 0x7f;
p = p ^ (p >> 4);
p = p ^ (p >> 2);
p = p ^ (p >> 1);
if (hour_one > 9 || hour_ten > 2 || (hour_ten == 2 && hour_one > 3) || (p & 1))
PDEBUG(DFRAME, DEBUG_INFO, "Hour : ???\n");
else {
hour = hour_ten * 10 + hour_one;
PDEBUG(DFRAME, DEBUG_INFO, "Hour : %02d\n", hour);
}
day_one = (frame >> 36 & 0xf);
day_ten = ((frame >> 40) & 0x3);
wday = (frame >> 42 & 0x7);
month_one = (frame >> 45 & 0xf);
month_ten = ((frame >> 49) & 0x1);
year_one = (frame >> 50 & 0xf);
year_ten = ((frame >> 54) & 0xf);
p = (frame >> 36) & 0x7fffff;
p = p ^ (p >> 16);
p = p ^ (p >> 8);
p = p ^ (p >> 4);
p = p ^ (p >> 2);
p = p ^ (p >> 1);
if (day_one > 9 || day_ten > 3 || (day_ten == 3 && day_one > 1) || (day_ten == 0 && day_one == 0) || (p & 1))
PDEBUG(DFRAME, DEBUG_INFO, "Day : ???\n");
else {
day = day_ten * 10 + day_one;
PDEBUG(DFRAME, DEBUG_INFO, "Day : %d\n", day);
}
if (wday < 1 || wday > 7 || (p & 1)) {
PDEBUG(DFRAME, DEBUG_INFO, "Week Day : ???\n");
wday = -1;
} else
PDEBUG(DFRAME, DEBUG_INFO, "Week Day : %s\n", week_day[wday]);
if (month_one > 9 || month_ten > 1 || (month_ten == 1 && month_one > 2) || (month_ten == 0 && month_one == 0) || (p & 1))
PDEBUG(DFRAME, DEBUG_INFO, "Month : ???\n");
else {
month = month_ten * 10 + month_one;
PDEBUG(DFRAME, DEBUG_INFO, "Month : %d\n", month);
}
if (year_one > 9 || year_ten > 9 || (p & 1))
PDEBUG(DFRAME, DEBUG_INFO, "Year : ???\n");
else {
year = year_ten * 10 + year_one;
PDEBUG(DFRAME, DEBUG_INFO, "Year : %02d\n", year);
}
if (minute >= 0 && hour >= 0 && day >= 0 && wday >= 0 && month >= 0 && year >= 0)
PDEBUG(DDCF77, DEBUG_NOTICE, "The received time is: %s %s %d %02d:%02d:00 %s 20%02d\n", week_day[wday], month_name[month], day, hour, minute, time_zone[zone], year);
else
PDEBUG(DDCF77, DEBUG_NOTICE, "The received time is invalid!\n");
}
static void rx_symbol(dcf77_t *dcf77, char symbol)
{
dcf77_rx_t *rx = &dcf77->rx;
PDEBUG(DDSP, DEBUG_DEBUG, "Received symbol '%c'\n", symbol);
if (!rx->data_receive) {
if (symbol == 'm') {
PDEBUG(DDSP, DEBUG_INFO, "Reception of frame has started\n");
rx->data_receive = 1;
rx->data_index = 0;
}
} else {
if (symbol == 'm') {
if (rx->data_index == 59) {
rx->data_string[rx->data_index] = '\0';
rx->data_index = 0;
PDEBUG(DDSP, DEBUG_INFO, "Received complete frame: %s (0x%016" PRIx64 ")\n", rx->data_string, rx->data_frame);
rx_frame(rx->data_frame);
} else {
PDEBUG(DDSP, DEBUG_INFO, "Short read, frame too short\n");
rx->data_index = 0;
}
} else {
if (rx->data_index == 59) {
PDEBUG(DDSP, DEBUG_INFO, "Long read, frame too long\n");
rx->data_receive = 0;
} else {
rx->data_string[rx->data_index++] = symbol;
rx->data_frame >>= 1;
rx->data_frame |= (uint64_t)(symbol & 1) << 58;
}
}
}
}
//#define DEBUG_SAMPLE
void dcf77_decode(dcf77_t *dcf77, sample_t *samples, int length)
{
dcf77_rx_t *rx = &dcf77->rx;
sample_t I[length], Q[length];
double phase, level, delayed_level, reduction, quality;
int i;
display_wave(&dcf77->dispwav, samples, length, 1.0);
if (!rx->enable)
return;
/* rotate spectrum */
phase = rx->carrier_phase;
for (i = 0; i < length; i++) {
/* mix with carrier frequency */
if (fast_math) {
I[i] = cos_tab[(uint16_t)phase] * samples[i];
Q[i] = sin_tab[(uint16_t)phase] * samples[i];
} else {
I[i] = cos(phase) * samples[i];
Q[i] = sin(phase) * samples[i];
}
phase += rx->carrier_phase_step;
if (phase >= rx->phase_360)
phase -= rx->phase_360;
}
rx->carrier_phase = phase;
level = sqrt(I[0] * I[0] + Q[0] * Q[0]);
if (level > 0.0) // don't average with level of 0.0 (-inf dB)
display_measurements_update(dcf77->dmp_input_level, level2db(level), 0.0);
/* filter carrier */
iir_process(&rx->carrier_lp[0], I, length);
iir_process(&rx->carrier_lp[1], Q, length);
for (i = 0; i < length; i++) {
rx->sample_counter += rx->sample_step;
if (rx->sample_counter >= 1.0) {
rx->sample_counter -= 1.0;
/* level */
level = sqrt(I[i] * I[i] + Q[i] * Q[i]);
if (level > 0.0) // don't average with level of 0.0 (-inf dB)
display_measurements_update(dcf77->dmp_signal_level, level2db(level), 0.0);
#ifdef DEBUG_SAMPLE
printf("%s amplitude= %.6f\n", debug_amplitude(level/rx->value_level), level/rx->value_level);
#endif
/* delay sample */
delayed_level = rx->delay_buffer[rx->delay_index];
rx->delay_buffer[rx->delay_index] = level;
if (++rx->delay_index == rx->delay_size)
rx->delay_index = 0;
if (rx->clock_count < 0 || rx->clock_count > 900) {
if (level / delayed_level < REDUCTION_TH)
rx->clock_count = 0;
}
if (rx->clock_count >= 0) {
if (rx->clock_count == 0) {
#ifdef DEBUG_SAMPLE
puts("got clock");
#endif
rx->value_level = delayed_level;
}
if (rx->clock_count == 50) {
#ifdef DEBUG_SAMPLE
puts("*short*");
#endif
rx->value_short = level;
reduction = rx->value_short / rx->value_level;
if (reduction < REDUCTION_TH) {
#ifdef DEBUG_SAMPLE
printf("reduction is %.3f\n", reduction);
#endif
if (reduction < REDUCTION_FACTOR)
reduction = REDUCTION_FACTOR;
quality = 1.0 - (reduction - REDUCTION_FACTOR) / (REDUCTION_TH - REDUCTION_FACTOR);
display_measurements_update(dcf77->dmp_signal_quality, quality * 100.0, 0.0);
}
}
if (rx->clock_count == 150) {
#ifdef DEBUG_SAMPLE
puts("*long*");
#endif
rx->value_long = level;
if (rx->value_long / rx->value_level < REDUCTION_TH)
rx_symbol(dcf77, '1');
else
rx_symbol(dcf77, '0');
}
if (rx->clock_count == 1100) {
#ifdef DEBUG_SAMPLE
puts("*missing clock*");
#endif
rx->clock_count = -1;
rx_symbol(dcf77, 'm');
}
}
if (rx->clock_count >= 0)
rx->clock_count++;
}
}
}

57
src/dcf77/dcf77.h Normal file
View File

@ -0,0 +1,57 @@
#include "../libsample/sample.h"
#include "../libfilter/iir_filter.h"
#include "../libdisplay/display.h"
#include <time.h>
typedef struct dcf77_tx {
int enable;
double phase_360;
double carrier_phase, carrier_phase_step; /* uncorrected phase */
double test_phase, test_phase_step;
double level;
int wave, waves_0, waves_1, waves_sec;
time_t timestamp;
int second;
char symbol;
uint64_t data_frame;
int test_tone;
} dcf77_tx_t;
typedef struct dcf77_rx {
int enable;
double phase_360;
double carrier_phase, carrier_phase_step; /* uncorrected phase */
iir_filter_t carrier_lp[2]; /* filters received carrier signal */
double sample_counter, sample_step; /* when to sample */
double *delay_buffer;
int delay_size, delay_index;
int clock_count;
double value_level, value_short, value_long; /* measured values */
int data_receive, data_index;
char data_string[60]; /* 59 digits + '\0' */
uint64_t data_frame;
iir_filter_t clock_lp[2]; /* filters received carrier signal */
} dcf77_rx_t;
typedef struct dcf77 {
dcf77_tx_t tx;
dcf77_rx_t rx;
/* measurements */
dispmeas_t dispmeas; /* display measurements */
dispmeasparam_t *dmp_input_level;
dispmeasparam_t *dmp_signal_level;
dispmeasparam_t *dmp_signal_quality;
/* wave */
dispwav_t dispwav; /* display wave form */
} dcf77_t;
int dcf77_init(int _fast_math);
void dcf77_exit(void);
dcf77_t *dcf77_create(int samplerate, int use_tx, int use_rx, int test_tone);
void dcf77_destroy(dcf77_t *dcf77);
void dcf77_tx_start(dcf77_t *dcf77, time_t timestamp);
void dcf77_encode(dcf77_t *dcf77, sample_t *samples, int length);
void dcf77_decode(dcf77_t *dcf77, sample_t *samples, int length);

513
src/dcf77/main.c Executable file
View File

@ -0,0 +1,513 @@
/* DCF77 main
*
* (C) 2022 by Andreas Eversberg <jolly@eversberg.eu>
* All Rights Reserved
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#include <stdio.h>
#include <unistd.h>
#include <errno.h>
#include <signal.h>
#include <string.h>
#include <stdint.h>
#include <stdlib.h>
#include <termios.h>
#include <sched.h>
#include <time.h>
#include "../libdebug/debug.h"
#include "../liboptions/options.h"
#include "../libsample/sample.h"
#include "../libsound/sound.h"
#include "dcf77.h"
int num_kanal = 1;
dcf77_t *dcf77 = NULL;
static void *soundif = NULL;
static const char *dsp_device = "";
static int dsp_samplerate = 192000;
static int dsp_buffer = 50;
static int rx = 0, tx = 0;
static time_t timestamp = -1;
static int double_amplitude = 0;
static int test_tone = 0;
static int dsp_interval = 1; /* ms */
static int rt_prio = 1;
static int fast_math = 0;
/* not static, in case we add libtimer some day, then compiler hits an error */
double get_time(void)
{
static struct timespec tv;
clock_gettime(CLOCK_REALTIME, &tv);
return (double)tv.tv_sec + (double)tv.tv_nsec / 1000000000.0;
}
static time_t parse_time(char **argv)
{
time_t t;
struct tm *tm;
int val;
t = get_time();
tm = localtime(&t);
if (!tm)
return -1;
val = atoi(argv[0]);
if (val < 1900)
return -1;
tm->tm_year = val - 1900;
val = atoi(argv[1]);
if (val < 1 || val > 12)
return -1;
tm->tm_mon = val - 1;
val = atoi(argv[2]);
if (val < 1 || val > 31)
return -1;
tm->tm_mday = val;
val = atoi(argv[3]);
if (val < 0 || val > 23)
return -1;
tm->tm_hour = val;
val = atoi(argv[4]);
if (val < 0 || val > 59)
return -1;
tm->tm_min = val;
val = atoi(argv[5]);
if (val < 0 || val > 59)
return -1;
tm->tm_sec = val;
tm->tm_isdst = -1;
return mktime(tm);
}
static time_t feierabend_time()
{
time_t t;
struct tm *tm;
t = get_time();
tm = localtime(&t);
if (!tm)
return -1;
tm->tm_hour = 17;
tm->tm_min = 0;
tm->tm_sec = 0;
tm->tm_isdst = -1;
return mktime(tm);
}
static void print_usage(const char *app)
{
printf("Usage: %s [-a hw:0,0] [<options>]\n", app);
}
void print_help(void)
{
/* - - */
printf(" -h --help\n");
printf(" This help\n");
printf(" --config [~/]<path to config file>\n");
printf(" Give a config file to use. If it starts with '~/', path is at home dir.\n");
printf(" Each line in config file is one option, '-' or '--' must not be given!\n");
debug_print_help();
printf(" -a --audio-device hw:<card>,<device>\n");
printf(" Sound card and device number (default = '%s')\n", dsp_device);
printf(" -s --samplerate <rate>\n");
printf(" Sample rate of sound device (default = '%d')\n", dsp_samplerate);
printf(" -b --buffer <ms>\n");
printf(" How many milliseconds are processed in advance (default = '%d')\n", dsp_buffer);
printf(" A buffer below 10 ms requires low interval like 0.1 ms.\n");
printf(" -T --tx\n");
printf(" Transmit time signal (default)\n");
printf(" -R --rx\n");
printf(" Receive time signal\n");
printf(" -F --fake\n");
printf(" Use given time stamp: <year> <month> <day> <hour> <min< <sec>.\n");
printf(" All values have to be numerical. The year must have 4 digits.\n");
printf(" --feierabend\n");
printf(" --end-of-working-day\n");
printf(" Use fake time stamp that equals 5 O'Clock PM.\n");
printf(" --geburtstag\n");
printf(" --birthday\n");
printf(" Use fake time stamp that equals birth of the author.\n");
printf(" -D --double-amplitude\n");
printf(" Transmit with double amplitude by using differential stereo output.\n");
printf(" --test-tone\n");
printf(" Transmit a test tone (10%% level, 1000 Hz) with the carrier.\n");
printf(" -r --realtime <prio>\n");
printf(" Set prio: 0 to disable, 99 for maximum (default = %d)\n", rt_prio);
printf(" --fast-math\n");
printf(" Use fast math approximation for slow CPU / ARM based systems.\n");
printf("\n");
printf("Press 'w' key to toggle display of RX wave form.\n");
printf("Press 'm' key to toggle display of measurement values.\n");
}
#define OPT_F1 1001
#define OPT_F2 1002
#define OPT_G1 1003
#define OPT_G2 1004
#define OPT_TEST_TONE 1005
#define OPT_FAST_MATH 1006
static void add_options(void)
{
option_add('h', "help", 0);
option_add('v', "verbose", 1);
option_add('a', "audio-device", 1);
option_add('s', "samplerate", 1);
option_add('b', "buffer", 1);
option_add('T', "tx", 0);
option_add('R', "rx", 0);
option_add('F', "fake", 6);
option_add(OPT_F1, "feierabend", 0);
option_add(OPT_F2, "end-of-working-day", 0);
option_add(OPT_G1, "geburtstag", 0);
option_add(OPT_G2, "birthday", 0);
option_add(OPT_TEST_TONE, "test-tone", 0);
option_add('D', "double-amplitude", 0);
option_add('r', "realtime", 1);
option_add(OPT_FAST_MATH, "fast-math", 0);
}
static int handle_options(int short_option, int argi, char **argv)
{
int rc;
switch (short_option) {
case 'h':
print_usage(argv[0]);
print_help();
return 0;
case 'v':
if (!strcasecmp(argv[argi], "list")) {
debug_list_cat();
return 0;
}
rc = parse_debug_opt(argv[argi]);
if (rc < 0) {
fprintf(stderr, "Failed to parse debug option, please use -h for help.\n");
return rc;
}
break;
case 'a':
dsp_device = options_strdup(argv[argi]);
break;
case 's':
dsp_samplerate = atoi(argv[argi]);
break;
case 'b':
dsp_buffer = atoi(argv[argi]);
break;
case 'T':
tx = 1;
break;
case 'R':
rx = 1;
break;
case 'F':
timestamp = parse_time(argv + argi);
printf("%ld\n",timestamp);
if (timestamp < 0) {
fprintf(stderr, "Given time stamp is invalid, please use -h for help.\n");
return -EINVAL;
}
break;
case OPT_F1:
case OPT_F2:
timestamp = feierabend_time() - 70;
break;
case OPT_G1:
case OPT_G2:
timestamp = 115099200 - 70;
break;
case OPT_TEST_TONE:
test_tone = 1;
break;
case 'D':
double_amplitude = 1;
break;
case 'r':
rt_prio = atoi(argv[argi]);
break;
case OPT_FAST_MATH:
fast_math = 1;
break;
default:
return -EINVAL;
}
return 1;
}
static int quit = 0;
static void sighandler(int sigset)
{
if (sigset == SIGHUP || sigset == SIGPIPE)
return;
fprintf(stderr, "\nSignal %d received.\n", sigset);
quit = 1;
}
static int get_char()
{
struct timeval tv = {0, 0};
fd_set fds;
char c = 0;
int __attribute__((__unused__)) rc;
FD_ZERO(&fds);
FD_SET(0, &fds);
select(0+1, &fds, NULL, NULL, &tv);
if (FD_ISSET(0, &fds)) {
rc = read(0, &c, 1);
return c;
} else
return -1;
}
int soundif_open(const char *audiodev, int samplerate, int buffer_size)
{
if (!audiodev || !audiodev[0]) {
PDEBUG(DDSP, DEBUG_ERROR, "No audio device given!\n");
return -EINVAL;
}
/* open audiodev */
soundif = sound_open(audiodev, NULL, NULL, NULL, (double_amplitude) ? 2 : 1, 0.0, samplerate, buffer_size, 1.0, 1.0, 0.0, 2.0);
if (!soundif) {
PDEBUG(DDSP, DEBUG_ERROR, "Failed to open sound device!\n");
return -EIO;
}
return 0;
}
void soundif_start(void)
{
sound_start(soundif);
PDEBUG(DDSP, DEBUG_DEBUG, "Starting audio stream!\n");
}
void soundif_close(void)
{
/* close audiodev */
if (soundif) {
sound_close(soundif);
soundif = NULL;
}
}
void soundif_work(int buffer_size)
{
int count;
sample_t buff1[buffer_size], buff2[buffer_size], *samples[2] = { buff1, buff2 };
double rf_level_db[2];
int rc;
int i;
/* encode and write */
count = sound_get_tosend(soundif, buffer_size);
if (count < 0) {
PDEBUG(DDSP, DEBUG_ERROR, "Failed to get number of samples in buffer (rc = %d)!\n", count);
return;
}
if (count) {
dcf77_encode(dcf77, samples[0], count);
if (double_amplitude) {
for (i = 0; i < count; i++)
samples[1][i] = -samples[0][i];
}
rc = sound_write(soundif, samples, NULL, count, NULL, NULL, (double_amplitude) ? 2 : 1);
if (rc < 0) {
PDEBUG(DDSP, DEBUG_ERROR, "Failed to write TX data to audio device (rc = %d)\n", rc);
return;
}
}
/* read */
count = sound_read(soundif, samples, buffer_size, 1, rf_level_db);
if (count < 0) {
PDEBUG(DDSP, DEBUG_ERROR, "Failed to read from audio device (rc = %d)!\n", count);
return;
}
/* decode */
dcf77_decode(dcf77, samples[0], count);
}
int main(int argc, char *argv[])
{
int rc, argi;
int buffer_size;
struct termios term, term_orig;
double begin_time, now, sleep;
char c;
/* handle options / config file */
add_options();
rc = options_config_file(argc, argv, "~/.osmocom/dcf77/dcf77.conf", handle_options);
if (rc < 0)
return 0;
argi = options_command_line(argc, argv, handle_options);
if (argi <= 0)
return argi;
if (dsp_samplerate < 192000) {
fprintf(stderr, "The sample rate must be at least 192000 to TX or RX 77.5 kHz. Quitting!\n");
goto error;
}
/* default to TX, if --tx and --rx was not set */
if (!tx && !rx)
tx = 1;
/* inits */
dcf77_init(fast_math);
/* size of dsp buffer in samples */
buffer_size = dsp_samplerate * dsp_buffer / 1000;
rc = soundif_open(dsp_device, dsp_samplerate, buffer_size);
if (rc < 0) {
printf("Failed to open sound for DCF77, use '-h' for help.\n");
goto error;
}
dcf77 = dcf77_create(dsp_samplerate, tx, rx, test_tone);
if (!dcf77) {
fprintf(stderr, "Failed to create \"DCF77\" instance. Quitting!\n");
goto error;
}
printf("\n");
printf("DCF77 ready.\n");
/* prepare terminal */
tcgetattr(0, &term_orig);
term = term_orig;
term.c_lflag &= ~(ISIG|ICANON|ECHO);
term.c_cc[VMIN]=1;
term.c_cc[VTIME]=2;
tcsetattr(0, TCSANOW, &term);
/* set real time prio */
if (rt_prio) {
struct sched_param schedp;
memset(&schedp, 0, sizeof(schedp));
schedp.sched_priority = rt_prio;
rc = sched_setscheduler(0, SCHED_RR, &schedp);
if (rc)
fprintf(stderr, "Error setting SCHED_RR with prio %d\n", rt_prio);
}
signal(SIGINT, sighandler);
signal(SIGHUP, sighandler);
signal(SIGTERM, sighandler);
signal(SIGPIPE, sighandler);
soundif_start();
if (tx)
dcf77_tx_start(dcf77, timestamp);
while (!quit) {
int w;
begin_time = get_time();
soundif_work(buffer_size);
do {
w = 0;
} while (w);
c = get_char();
switch (c) {
case 3:
printf("CTRL+c received, quitting!\n");
quit = 1;
break;
case 'w':
/* toggle wave display */
display_measurements_on(0);
display_wave_on(-1);
break;
case 'm':
/* toggle measurements display */
display_wave_on(0);
display_measurements_on(-1);
break;
default:
break;
}
display_measurements(dsp_interval / 1000.0);
now = get_time();
/* sleep interval */
sleep = ((double)dsp_interval / 1000.0) - (now - begin_time);
if (sleep > 0)
usleep(sleep * 1000000.0);
}
signal(SIGINT, SIG_DFL);
signal(SIGTSTP, SIG_DFL);
signal(SIGHUP, SIG_DFL);
signal(SIGTERM, SIG_DFL);
signal(SIGPIPE, SIG_DFL);
/* reset real time prio */
if (rt_prio > 0) {
struct sched_param schedp;
memset(&schedp, 0, sizeof(schedp));
schedp.sched_priority = 0;
sched_setscheduler(0, SCHED_OTHER, &schedp);
}
/* reset terminal */
tcsetattr(0, TCSANOW, &term_orig);
error:
/* destroy UK0 instances */
if (dcf77)
dcf77_destroy(dcf77);
soundif_close();
dcf77_exit();
options_free();
return 0;
}

View File

@ -90,6 +90,9 @@ struct debug_cat {
{ "dss1", "\033[1;34m" },
{ "sip", "\033[1;35m" },
{ "telephone", "\033[1;34m" },
{ "uk0", "\033[1;34m" },
{ "ph", "\033[0;33m" },
{ "dcf77", "\033[1;34m" },
{ NULL, NULL }
};
@ -187,7 +190,7 @@ void _printdebug(const char *file, const char __attribute__((unused)) *function,
printf("%04d-%02d-%02d %02d:%02d:%02d.%03d ", tm->tm_year + 1900, tm->tm_mon + 1, tm->tm_mday, tm->tm_hour, tm->tm_min, tm->tm_sec, (int)(tv.tv_usec / 10000.0));
}
printf("%s%s:%4d %s: %s\033[0;39m", debug_cat[cat].color, file, line, debug_level[level], buffer);
printf("%s%s %4d %s-%s: %s\033[0;39m", debug_cat[cat].color, file, line, debug_cat[cat].name, debug_level[level], buffer);
if (debug_limit_scroll)
printf("\0337\033[%d;%dr\0338", 1, h);
if (print_console_text)

View File

@ -52,6 +52,9 @@
#define DDSS1 45
#define DSIP 46
#define DTEL 47
#define DUK0 48
#define DPH 49
#define DDCF77 50
void lock_debug(void);
void unlock_debug(void);

View File

@ -33,7 +33,8 @@
}
#define _OSMO_CC_NAME2VALUE(array) { \
for (int value = 0; (size_t)value < (sizeof(array) / sizeof(array[0])); value++) { \
int value; \
for (value = 0; (size_t)value < (sizeof(array) / sizeof(array[0])); value++) { \
if (!strcasecmp(array[value], name)) \
return value; \
} \

View File

@ -29,7 +29,7 @@
#include "../liboptions/options.h"
#include "endpoint.h"
#define NTP_OFFSET 2208988800
#define NTP_OFFSET 2208988800U
void osmo_cc_set_local_peer(osmo_cc_session_config_t *conf, enum osmo_cc_session_nettype nettype, enum osmo_cc_session_addrtype addrtype, const char *address)
{

View File

@ -997,6 +997,8 @@ int sdr_read(void *inst, sample_t **samples, int num, int channels, double *rf_l
if (channels) {
for (c = 0; c < channels; c++) {
if (rf_level_db)
rf_level_db[c] = NAN;
if (sdr->chan[c].am)
am_demodulate_complex(&sdr->chan[c].am_demod, samples[c], count, buff, sdr->modbuff_I, sdr->modbuff_Q, sdr->modbuff_carrier);
else
@ -1013,7 +1015,8 @@ int sdr_read(void *inst, sample_t **samples, int num, int channels, double *rf_l
avg = sqrt(avg /(double)count); /* RMS */
avg = log10(avg) * 20;
display_measurements_update(sdr->chan[c].dmp_rf_level, avg, 0.0);
rf_level_db[c] = avg;
if (rf_level_db)
rf_level_db[c] = avg;
if (!sdr->chan[c].am) {
min = 0.0;
max = 0.0;

View File

@ -29,6 +29,8 @@
#include "sound.h"
#endif
static int KEEP_FRAMES=8; /* minimum frames not to read, to prevent reading from buffer before data has been received (seems to be a bug in ALSA) */
typedef struct sound {
snd_pcm_t *phandle, *chandle;
int pchannels, cchannels;
@ -190,6 +192,7 @@ static void dev_close(sound_t *sound)
void *sound_open(const char *audiodev, double __attribute__((unused)) *tx_frequency, double __attribute__((unused)) *rx_frequency, int __attribute__((unused)) *am, int channels, double __attribute__((unused)) paging_frequency, int samplerate, int __attribute((unused)) buffer_size, double __attribute__((unused)) interval, double max_deviation, double __attribute__((unused)) max_modulation, double __attribute__((unused)) modulation_index)
{
sound_t *sound;
const char *env;
int rc;
if (channels < 1 || channels > 2) {
@ -229,6 +232,11 @@ void *sound_open(const char *audiodev, double __attribute__((unused)) *tx_freque
}
#endif
if ((env = getenv("KEEP_FRAMES"))) {
KEEP_FRAMES = atoi(env);
PDEBUG(DSOUND, DEBUG_NOTICE, "KEEP %d samples in RX buffer, to prevent corrupt read.\n", KEEP_FRAMES);
}
return sound;
error:
@ -387,9 +395,7 @@ int sound_write(void *inst, sample_t **samples, uint8_t __attribute__((unused))
return rc;
}
#define KEEP_FRAMES 8 /* minimum frames not to read, due to bug in ALSA */
int sound_read(void *inst, sample_t **samples, int num, int channels, double __attribute__((unused)) *rf_level_db)
int sound_read(void *inst, sample_t **samples, int num, int channels, double *rf_level_db)
{
sound_t *sound = (sound_t *)inst;
double spl_deviation = sound->spl_deviation;
@ -399,9 +405,6 @@ int sound_read(void *inst, sample_t **samples, int num, int channels, double __a
int in, rc;
int i, ii;
/* make valgrind happy, because snd_pcm_readi() does not seem to initially fill buffer with values */
memset(buff, 0, sizeof(buff));
/* get samples in rx buffer */
in = snd_pcm_avail(sound->chandle);
/* if not more than KEEP_FRAMES frames available, try next time */
@ -413,8 +416,10 @@ int sound_read(void *inst, sample_t **samples, int num, int channels, double __a
if (in > num)
in = num;
rc = snd_pcm_readi(sound->chandle, buff, in);
/* make valgrind happy, because snd_pcm_readi() does not seem to initially fill buffer with values */
memset(buff, 0, sizeof(*buff) * sound->cchannels * in);
rc = snd_pcm_readi(sound->chandle, buff, in);
if (rc < 0) {
if (errno == EAGAIN)
return 0;
@ -430,10 +435,8 @@ int sound_read(void *inst, sample_t **samples, int num, int channels, double __a
}
return rc;
}
if (rc == 0)
return rc;
if (sound->cchannels == 2) {
if (channels < 2) {
for (i = 0, ii = 0; i < rc; i++) {
@ -471,12 +474,17 @@ int sound_read(void *inst, sample_t **samples, int num, int channels, double __a
#ifdef HAVE_MOBILE
sender_t *sender;
for (i = 0; i < channels; i++) {
if (rf_level_db)
rf_level_db[i] = NAN;
sender = get_sender_by_empfangsfrequenz(sound->rx_frequency[i]);
if (!sender)
continue;
display_measurements_update(sound->dmp[i], log10((double)max[i] / 32768.0) * 20, 0.0);
}
#else
for (i = 0; i < channels; i++) {
if (rf_level_db)
rf_level_db[i] = 0.0;
rf_level_db[i] = NAN;
}
#endif
@ -512,6 +520,8 @@ int sound_get_tosend(void *inst, int buffer_size)
}
tosend = buffer_size - delay;
if (tosend < 0)
tosend = 0;
return tosend;
}

View File

@ -1627,7 +1627,7 @@ const char *nmt_get_frame(nmt_t *nmt)
{
frame_t frame;
const char *bits;
int debug = 1;
int last_frame_idle, debug = 1;
memset(&frame, 0, sizeof(frame));
@ -1681,13 +1681,18 @@ const char *nmt_get_frame(nmt_t *nmt)
break;
}
last_frame_idle = nmt->tx_last_frame_idle;
nmt->tx_last_frame_idle = 0;
/* no encoding debug for certain (idle) frames */
switch(frame.mt) {
case NMT_MESSAGE_1a:
case NMT_MESSAGE_4:
case NMT_MESSAGE_1b:
case NMT_MESSAGE_30:
debug = 0;
if (last_frame_idle)
debug = 0;
nmt->tx_last_frame_idle = 1;
break;
default:
break;
@ -1699,7 +1704,10 @@ const char *nmt_get_frame(nmt_t *nmt)
bits = encode_frame(nmt->sysinfo.system, &frame, debug);
PDEBUG_CHAN(DNMT, DEBUG_DEBUG, "Sending frame %s.\n", nmt_frame_name(frame.mt));
if (debug)
PDEBUG_CHAN(DNMT, DEBUG_DEBUG, "Sending frame %s.\n", nmt_frame_name(frame.mt));
if (debug && nmt->tx_last_frame_idle)
PDEBUG_CHAN(DNMT, DEBUG_DEBUG, "Subsequent IDLE frames are not show, to prevent flooding the output.\n");
return bits;
}

View File

@ -131,6 +131,7 @@ struct nmt {
char tx_frame[166]; /* carries bits of one frame to transmit */
int tx_frame_length;
int tx_frame_pos;
int tx_last_frame_idle; /* indicator to prevent debugging all idle frames */
/* DMS/SMS states */
dms_t dms; /* DMS states */

View File

@ -1153,7 +1153,7 @@ const char *r2000_get_frame(r2000_t *r2000)
{
frame_t frame;
const char *bits;
int debug = 1;
int last_frame_idle, debug = 1;
r2000->tx_frame_count++;
@ -1165,11 +1165,16 @@ const char *r2000_get_frame(r2000_t *r2000)
frame.sm_power = r2000->sysinfo.sm_power;
frame.taxe = r2000->sysinfo.taxe;
last_frame_idle = r2000->tx_last_frame_idle;
r2000->tx_last_frame_idle = 0;
switch (r2000->state) {
case STATE_IDLE:
case STATE_RECALL_WAIT:
tx_idle(r2000, &frame);
debug = 0;
if (last_frame_idle)
debug = 0;
r2000->tx_last_frame_idle = 1;
break;
case STATE_INSCRIPTION:
tx_inscription(r2000, &frame);
@ -1217,7 +1222,10 @@ const char *r2000_get_frame(r2000_t *r2000)
bits = encode_frame(&frame, debug);
PDEBUG_CHAN(DR2000, DEBUG_DEBUG, "Sending frame %s.\n", r2000_frame_name(frame.message, REL_TO_SM));
if (debug)
PDEBUG_CHAN(DR2000, DEBUG_DEBUG, "Sending frame %s.\n", r2000_frame_name(frame.message, REL_TO_SM));
if (debug && r2000->tx_last_frame_idle)
PDEBUG_CHAN(DR2000, DEBUG_DEBUG, "Subsequent IDLE frames are not show, to prevent flooding the output.\n");
return bits;
}

View File

@ -90,6 +90,7 @@ typedef struct r2000 {
char tx_frame[208]; /* carries bits of one frame to transmit */
int tx_frame_length;
int tx_frame_pos;
int tx_last_frame_idle; /* indicator to prevent debugging all idle frames */
uint16_t rx_sync; /* shift register to detect sync */
int rx_in_sync; /* if we are in sync and receive bits */
int rx_mute; /* mute count down after sync */