Add SID 16420 #6

Closed
shadowcaster3 wants to merge 31 commits from (deleted):master into master
116 changed files with 130660 additions and 126292 deletions

5
.gitignore vendored
View File

@ -62,8 +62,8 @@ src/nmt/nmt
src/amps/libusatone.a
src/amps/libamps.a
src/amps/amps
src/tacs/tacs
src/jtacs/jtacs
src/amps/tacs
src/amps/jtacs
src/r2000/radiocom2000
src/imts/imts
src/imts/imts-dialer
@ -71,6 +71,7 @@ src/mpt1327/mpt1327
src/jolly/jollycom
src/eurosignal/eurosignal
src/pocsag/pocsag
src/golay/golay
src/fuenf/5-ton-folge
src/tv/osmotv
src/radio/osmoradio

13
README
View File

@ -24,10 +24,13 @@ Additionally the following communication services are implemented:
* TV Transmitter with test Images
* Radio transmitter / receiver
* Analog Modem Emulation (AM7911)
* Analog Modem Emulation 'Datenklo' (AM7911)
* German classic 'Zeitansage' (talking clock)
* POCSAG transmitter / receiver
* DCF77 time signal transmitter and receiver
* Golay/GSC transmitter / receiver
* DCF77 time signal transmitter and receiver with weather info
* C-Netz SIM emulator
* C-Netz magnetic card emulator
USE AT YOUR OWN RISK!
@ -66,10 +69,10 @@ providing memory cards to be programmed for older C-Netz phone.
Dieter Spaar providing TACS recordings to verify and debug TACS support.
Hans Wigger providing Radiocom 2000 recordings, to reverse-enigeer the standard,
which seems not to exist anymore...
Hans Wigger providing Radiocom 2000 recordings, to reverse-enigineer the
standard, which seems not to exist anymore...
Peter, Peter and Friedhelm and Stephan for providing documentation and hardware
Peter, Peter, Friedhelm and Stephan for providing documentation and hardware
for C-Netz Base Station and other C-Netz documents.
Carsten Wollesen for donating MPT1327 radios and programming tools.

View File

@ -98,14 +98,13 @@ AC_OUTPUT(
src/cnetz/Makefile
src/nmt/Makefile
src/amps/Makefile
src/tacs/Makefile
src/jtacs/Makefile
src/r2000/Makefile
src/imts/Makefile
src/mpt1327/Makefile
src/jolly/Makefile
src/eurosignal/Makefile
src/pocsag/Makefile
src/golay/Makefile
src/fuenf/Makefile
src/tv/Makefile
src/radio/Makefile

View File

@ -129,8 +129,10 @@ Additional features:
<li><a href="magnetic.html">C-Netz Magnetic Card</a></li>
<li>Zeitansage (German talking clock)</li>
<li>C-Netz FuVSt (MSC to control a real base station)</li>
<li>POCSAG</li>
<li>POCSAG (paging system)</li>
<li>Golay / GSC (paging system)</li>
<li>5-Ton-Ruf (firefighter's pagers and siren control)</li>
<li>DCF77 The German longwave time signal transmitter/receiver</li>
</ul>
</td></tr></table></center>

View File

@ -48,14 +48,13 @@ SUBDIRS += \
cnetz \
nmt \
amps \
tacs \
jtacs \
r2000 \
imts \
mpt1327 \
jolly \
eurosignal \
pocsag \
golay \
fuenf \
tv \
radio \

View File

@ -1,12 +1,12 @@
AM_CPPFLAGS = -Wall -Wextra -g $(all_includes)
bin_PROGRAMS = \
amps
amps tacs jtacs
noinst_LIBRARIES = libamps.a libusatone.a
libamps_a_SOURCES = \
amps_tacs_main.c \
main_common.c \
amps.c \
transaction.c \
frame.c \
@ -14,16 +14,16 @@ libamps_a_SOURCES = \
sysinfo.c
libusatone_a_SOURCES = \
tones.c \
noanswer.c \
outoforder.c \
invalidnumber.c \
congestion.c
usa_tones.c \
usa_noanswer.c \
usa_outoforder.c \
usa_invalidnumber.c \
usa_congestion.c
amps_SOURCES = \
stations.c \
image.c \
main.c
amps_stations.c \
amps_image.c \
amps_main.c
amps_LDADD = \
$(COMMON_LA) \
@ -63,3 +63,90 @@ amps_LDADD += \
$(SOAPY_LIBS)
endif
tacs_SOURCES = \
tacs_tones.c \
tacs_outoforder.c \
tacs_stations.c \
tacs_image.c \
tacs_main.c
tacs_LDADD = \
$(COMMON_LA) \
libamps.a \
$(top_builddir)/src/liboptions/liboptions.a \
$(top_builddir)/src/libdebug/libdebug.a \
$(top_builddir)/src/libmobile/libmobile.a \
$(top_builddir)/src/libosmocc/libosmocc.a \
$(top_builddir)/src/libdisplay/libdisplay.a \
$(top_builddir)/src/libcompandor/libcompandor.a \
$(top_builddir)/src/libgoertzel/libgoertzel.a \
$(top_builddir)/src/libjitter/libjitter.a \
$(top_builddir)/src/libtimer/libtimer.a \
$(top_builddir)/src/libsamplerate/libsamplerate.a \
$(top_builddir)/src/libemphasis/libemphasis.a \
$(top_builddir)/src/libfm/libfm.a \
$(top_builddir)/src/libfilter/libfilter.a \
$(top_builddir)/src/libwave/libwave.a \
$(top_builddir)/src/libsample/libsample.a \
$(top_builddir)/src/libg711/libg711.a \
$(top_builddir)/src/libaaimage/libaaimage.a \
-lm
if HAVE_ALSA
tacs_LDADD += \
$(top_builddir)/src/libsound/libsound.a \
$(ALSA_LIBS)
endif
if HAVE_SDR
tacs_LDADD += \
$(top_builddir)/src/libsdr/libsdr.a \
$(top_builddir)/src/libam/libam.a \
$(top_builddir)/src/libfft/libfft.a \
$(UHD_LIBS) \
$(SOAPY_LIBS)
endif
jtacs_SOURCES = \
jtacs_tones.c \
jtacs_stations.c \
jtacs_image.c \
jtacs_main.c
jtacs_LDADD = \
$(COMMON_LA) \
libamps.a \
$(top_builddir)/src/liboptions/liboptions.a \
$(top_builddir)/src/libdebug/libdebug.a \
$(top_builddir)/src/libmobile/libmobile.a \
$(top_builddir)/src/libosmocc/libosmocc.a \
$(top_builddir)/src/libdisplay/libdisplay.a \
$(top_builddir)/src/libcompandor/libcompandor.a \
$(top_builddir)/src/libgoertzel/libgoertzel.a \
$(top_builddir)/src/libjitter/libjitter.a \
$(top_builddir)/src/libtimer/libtimer.a \
$(top_builddir)/src/libsamplerate/libsamplerate.a \
$(top_builddir)/src/libemphasis/libemphasis.a \
$(top_builddir)/src/libfm/libfm.a \
$(top_builddir)/src/libfilter/libfilter.a \
$(top_builddir)/src/libwave/libwave.a \
$(top_builddir)/src/libsample/libsample.a \
$(top_builddir)/src/libg711/libg711.a \
$(top_builddir)/src/libaaimage/libaaimage.a \
-lm
if HAVE_ALSA
jtacs_LDADD += \
$(top_builddir)/src/libsound/libsound.a \
$(ALSA_LIBS)
endif
if HAVE_SDR
jtacs_LDADD += \
$(top_builddir)/src/libsdr/libsdr.a \
$(top_builddir)/src/libam/libam.a \
$(top_builddir)/src/libfft/libfft.a \
$(UHD_LIBS) \
$(SOAPY_LIBS)
endif

View File

@ -61,7 +61,9 @@
#define PAGE_TRIES 2 /* how many times to page the phone */
#define PAGE_TO1 8.0 /* max time to wait for paging reply */
#define PAGE_TO2 4.0 /* max time to wait for last paging reply */
#define ALERT_TO 60.0 /* max time to wait for answer */
#define ALERT_TRIES 3 /* how many times to alert the phone */
#define ALERT_TO 0.6 /* max time to wait for alert confirm */
#define ANSWER_TO 60.0 /* max time to wait for answer */
#define RELEASE_TIMER 5.0 /* max time to send release messages */
/* Convert channel number to frequency number of base station.
@ -103,7 +105,7 @@ double amps_channel2freq(int channel, int uplink)
/* JTACS */
/* see "ARIB_STD-T64-C.S0057-0v1.0.pdf" */
if (uplink == 2)
return -55.000 * 1e6;
return 55.000 * 1e6;
/* 799 channels */
if (channel >= 1 && channel <= 799)
@ -128,13 +130,19 @@ double amps_channel2freq(int channel, int uplink)
enum amps_chan_type amps_channel2type(int channel)
{
if (!tacs) {
/* AMPS */
if (channel >= 313 && channel <= 354)
return CHAN_TYPE_CC;
} else {
} else if (!jtacs) {
/* TACS */
if (channel >= 23 && channel <= 43)
return CHAN_TYPE_CC;
if (channel >= 323 && channel <= 343)
return CHAN_TYPE_CC;
} else {
/* JTACS */
if (channel >= 418 && channel <= 456)
return CHAN_TYPE_CC;
}
return CHAN_TYPE_VC;
@ -143,6 +151,7 @@ enum amps_chan_type amps_channel2type(int channel)
const char *amps_channel2band(int channel)
{
if (!tacs) {
/* AMPS */
if (channel >= 991 && channel <= 1023)
return "A''";
if (channel >= 1 && channel <= 333)
@ -153,11 +162,15 @@ const char *amps_channel2band(int channel)
return "A'";
if (channel >= 717 && channel <= 799)
return "B'";
} else {
} else if (!jtacs) {
/* TACS */
if (channel >= 1 && channel <= 300)
return "A";
if (channel >= 301 && channel <= 600)
return "B";
} else {
/* JTACS */
return "A";
}
return "<invalid>";
@ -211,12 +224,12 @@ void amps_number2min(const char *number, uint32_t *min1, uint16_t *min2)
}
if (!tacs) {
/* MIN1 */
/* MIN1 (amps) */
*min1 = ((uint32_t)(digit2binary(number[0]) * 100 + digit2binary(number[1]) * 10 + digit2binary(number[2]) - 111)) << 14;
*min1 |= digit2binary(number[3]) << 10;
*min1 |= digit2binary(number[4]) * 100 + digit2binary(number[5]) * 10 + digit2binary(number[6]) - 111;
} else {
/* MIN1 */
/* MIN1 (tacs/jtacs) */
*min1 = digit2binary(number[0]) << 20;
*min1 |= (digit2binary(number[1]) * 100 + digit2binary(number[2]) * 10 + digit2binary(number[3]) - 111) << 10;
*min1 |= digit2binary(number[4]) * 100 + digit2binary(number[5]) * 10 + digit2binary(number[6]) - 111;
@ -227,6 +240,8 @@ void amps_number2min(const char *number, uint32_t *min1, uint16_t *min2)
*/
/* TACS: convert MIN1 and MIN2 to AREA-XXXXXXX
*/
/* JTACS: convert MIN1 and MIN2 to NET-XXXXXXX (NET = mobile network code, always 440)
*/
const char *amps_min22number(uint16_t min2)
{
static char number[4];
@ -249,7 +264,7 @@ const char *amps_min12number(uint32_t min1)
static char number[8];
if (!tacs) {
/* MIN1 */
/* MIN1 (amps) */
if ((min1 >> 14) > 999)
strcpy(number, "???");
else {
@ -269,7 +284,7 @@ const char *amps_min12number(uint32_t min1)
number[6] = binary2digit(((min1 & 0x3ff) % 10) + 1);
}
} else {
/* MIN1 */
/* MIN1 (tacs/jtacs) */
if ((min1 >> 20) < 1 || (min1 >> 20) > 10)
number[0] = '?';
else
@ -502,7 +517,7 @@ static amps_t *search_pc(void)
}
/* Create transceiver instance and link to a list. */
int amps_create(const char *kanal, enum amps_chan_type chan_type, const char *device, int use_sdr, int samplerate, double rx_gain, double tx_gain, 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, amps_si *si, uint16_t sid, uint8_t sat, int polarity, int tolerant, int loopback)
int amps_create(const char *kanal, enum amps_chan_type chan_type, const char *device, int use_sdr, int samplerate, double rx_gain, double tx_gain, 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, amps_si *si, uint16_t sid, uint8_t sat, int polarity, int send_callerid, int tolerant, int loopback)
{
sender_t *sender;
amps_t *amps;
@ -513,6 +528,8 @@ int amps_create(const char *kanal, enum amps_chan_type chan_type, const char *de
/* check for channel number */
if (amps_channel2freq(atoi(kanal), 0) == 0) {
PDEBUG(DAMPS, DEBUG_ERROR, "Channel number %s invalid.\n", kanal);
if (jtacs)
PDEBUG(DAMPS, DEBUG_ERROR, "Try an even channel number, like 440.\n");
return -EINVAL;
}
@ -542,6 +559,11 @@ int amps_create(const char *kanal, enum amps_chan_type chan_type, const char *de
PDEBUG(DAMPS, DEBUG_ERROR, "Channel number %s belongs to a voice channel, but your channel type '%s' requires to be on a control channel number. Please use correct channel.\n", kanal, chan_type_long_name(chan_type));
return -EINVAL;
}
/* only even channels */
if (jtacs && chan_type != CHAN_TYPE_VC && (atoi(kanal) & 1)) {
PDEBUG(DAMPS, DEBUG_ERROR, "Control channel on JTACS system seem not to work with odd channel numbers. Please use even channel number.\n");
return -EINVAL;
}
/* check if sid machtes channel band */
band = amps_channel2band(atoi(kanal));
@ -582,12 +604,12 @@ int amps_create(const char *kanal, enum amps_chan_type chan_type, const char *de
amps->chan_type = chan_type;
memcpy(&amps->si, si, sizeof(amps->si));
amps->sat = sat;
amps->send_callerid = send_callerid;
if (polarity < 0)
amps->flip_polarity = 1;
amps->pre_emphasis = pre_emphasis;
amps->de_emphasis = de_emphasis;
/* the AMPS uses a frequency rage of 300..3000 Hz, but we still use the default low pass filter, which is not too far above */
rc = init_emphasis(&amps->estate, samplerate, CUT_OFF_EMPHASIS_DEFAULT, CUT_OFF_HIGHPASS_DEFAULT, CUT_OFF_LOWPASS_DEFAULT);
if (rc < 0)
@ -688,14 +710,13 @@ static void amps_release(transaction_t *trans, uint8_t cause)
trans->callref = 0;
}
/* change DSP mode to transmit release */
if (amps->dsp_mode == DSP_MODE_AUDIO_RX_AUDIO_TX || amps->dsp_mode == DSP_MODE_OFF)
if (amps->dsp_mode == DSP_MODE_AUDIO_RX_AUDIO_TX || amps->dsp_mode == DSP_MODE_AUDIO_RX_SILENCE_TX || amps->dsp_mode == DSP_MODE_OFF)
amps_set_dsp_mode(amps, DSP_MODE_AUDIO_RX_FRAME_TX, 0);
}
/*
* receive signaling
*/
void amps_rx_signaling_tone(amps_t *amps, int tone, double quality)
{
transaction_t *trans = amps->trans_list;
@ -710,6 +731,7 @@ void amps_rx_signaling_tone(amps_t *amps, int tone, double quality)
PDEBUG_CHAN(DAMPS, DEBUG_INFO, "Lost Signaling Tone signal\n");
switch (trans->state) {
case TRANS_CALL_MO_ASSIGN_CONFIRM: // should not happen
case TRANS_CALL:
if (!tone)
break;
@ -723,16 +745,19 @@ void amps_rx_signaling_tone(amps_t *amps, int tone, double quality)
destroy_transaction(trans);
amps_go_idle(amps);
break;
case TRANS_CALL_MT_ALERT:
case TRANS_CALL_MT_ASSIGN_CONFIRM: // should not happen
case TRANS_CALL_MT_ALERT: // should not happen
case TRANS_CALL_MT_ALERT_SEND: // should not happen
case TRANS_CALL_MT_ALERT_CONFIRM:
if (tone) {
timer_stop(&trans->timer);
call_up_alerting(trans->callref);
amps_set_dsp_mode(amps, DSP_MODE_AUDIO_RX_AUDIO_TX, 0);
trans_new_state(trans, TRANS_CALL_MT_ALERT_SEND);
timer_start(&trans->timer, ALERT_TO);
trans_new_state(trans, TRANS_CALL_MT_ANSWER_WAIT);
timer_start(&trans->timer, ANSWER_TO);
}
break;
case TRANS_CALL_MT_ALERT_SEND:
case TRANS_CALL_MT_ANSWER_WAIT:
if (!tone) {
timer_stop(&trans->timer);
if (!trans->sat_detected)
@ -753,13 +778,20 @@ void amps_rx_sat(amps_t *amps, int tone, double quality)
PDEBUG_CHAN(DAMPS, DEBUG_ERROR, "SAT signal without transaction, please fix!\n");
return;
}
/* irgnoring SAT loss on release */
if (trans->state == TRANS_CALL_RELEASE
|| trans->state == TRANS_CALL_RELEASE_SEND)
return;
if (trans->state != TRANS_CALL
/* only SAT with these states */
if (trans->state != TRANS_CALL_MO_ASSIGN_CONFIRM
&& trans->state != TRANS_CALL_MT_ASSIGN_CONFIRM
&& trans->state != TRANS_CALL_MT_ALERT
&& trans->state != TRANS_CALL_MT_ALERT_SEND) {
&& trans->state != TRANS_CALL_MT_ALERT_SEND
&& trans->state != TRANS_CALL_MT_ALERT_CONFIRM
&& trans->state != TRANS_CALL_MT_ANSWER_WAIT
&& trans->state != TRANS_CALL) {
PDEBUG_CHAN(DAMPS, DEBUG_ERROR, "SAT signal without active call, please fix!\n");
return;
}
@ -772,10 +804,20 @@ void amps_rx_sat(amps_t *amps, int tone, double quality)
trans->sat_detected = 0;
}
/* no SAT during alerting */
if (trans->state == TRANS_CALL_MT_ALERT
|| trans->state == TRANS_CALL_MT_ALERT_SEND)
return;
/* initial SAT received */
if (tone && trans->state == TRANS_CALL_MO_ASSIGN_CONFIRM) {
PDEBUG_CHAN(DAMPS, DEBUG_INFO, "Confirm from mobile (SAT) received\n");
timer_stop(&trans->timer);
trans_new_state(trans, TRANS_CALL);
amps_set_dsp_mode(amps, DSP_MODE_AUDIO_RX_AUDIO_TX, 0);
}
if (tone && trans->state == TRANS_CALL_MT_ASSIGN_CONFIRM) {
PDEBUG_CHAN(DAMPS, DEBUG_INFO, "Confirm from mobile (SAT) received\n");
timer_stop(&trans->timer);
trans->alert_retry = 1;
trans_new_state(trans, TRANS_CALL_MT_ALERT);
amps_set_dsp_mode(amps, DSP_MODE_AUDIO_RX_FRAME_TX, 0);
}
if (tone) {
timer_stop(&trans->timer);
@ -790,20 +832,6 @@ void amps_rx_sat(amps_t *amps, int tone, double quality)
return;
}
static void timeout_sat(amps_t *amps, double duration)
{
if (!amps->trans_list) {
PDEBUG_CHAN(DAMPS, DEBUG_ERROR, "SAT timeout, but no transaction, please fix!\n");
return;
}
if (duration == SAT_TO1)
PDEBUG_CHAN(DAMPS, DEBUG_NOTICE, "Timeout after %.0f seconds not receiving SAT signal.\n", duration);
else
PDEBUG_CHAN(DAMPS, DEBUG_NOTICE, "Timeout after %.0f seconds loosing SAT signal.\n", duration);
PDEBUG_CHAN(DAMPS, DEBUG_INFO, "Release call towards network.\n");
amps_release(amps->trans_list, CAUSE_TEMPFAIL);
}
/* receive message from phone on RECC */
void amps_rx_recc(amps_t *amps, uint8_t scm, uint8_t mpci, uint32_t esn, uint32_t min1, uint16_t min2, uint8_t msg_type, uint8_t ordq, uint8_t order, const char *dialing)
{
@ -950,6 +978,11 @@ int call_down_setup(int callref, const char __attribute__((unused)) *caller_id,
}
trans->callref = callref;
trans->page_retry = 1;
if (caller_type == TYPE_INTERNATIONAL) {
trans->caller_id[0] = '+';
strncpy(trans->caller_id + 1, caller_id, sizeof(trans->caller_id) - 2);
} else
strncpy(trans->caller_id, caller_id, sizeof(trans->caller_id) - 1);
return 0;
}
@ -988,8 +1021,11 @@ void call_down_disconnect(int callref, int cause)
switch (amps->dsp_mode) {
case DSP_MODE_AUDIO_RX_AUDIO_TX:
case DSP_MODE_AUDIO_RX_FRAME_TX:
if (trans->state == TRANS_CALL_MT_ALERT
|| trans->state == TRANS_CALL_MT_ALERT_SEND) {
if (trans->state == TRANS_CALL_MT_ASSIGN_CONFIRM
|| trans->state == TRANS_CALL_MT_ALERT
|| trans->state == TRANS_CALL_MT_ALERT_SEND
|| trans->state == TRANS_CALL_MT_ALERT_CONFIRM
|| trans->state == TRANS_CALL_MT_ANSWER_WAIT) {
PDEBUG_CHAN(DAMPS, DEBUG_INFO, "Call control disconnect on voice channel while alerting, releasing towards mobile station.\n");
amps_release(trans, cause);
}
@ -1028,6 +1064,7 @@ void call_down_release(int callref, int cause)
trans->callref = 0;
switch (amps->dsp_mode) {
case DSP_MODE_AUDIO_RX_SILENCE_TX:
case DSP_MODE_AUDIO_RX_AUDIO_TX:
case DSP_MODE_AUDIO_RX_FRAME_TX:
PDEBUG_CHAN(DAMPS, DEBUG_INFO, "Call control releases on voice channel, releasing towards mobile station.\n");
@ -1041,7 +1078,7 @@ void call_down_release(int callref, int cause)
}
/* Receive audio from call instance. */
void call_down_audio(int callref, sample_t *samples, int count)
void call_down_audio(int callref, uint16_t sequence, uint32_t timestamp, uint32_t ssrc, sample_t *samples, int count)
{
sender_t *sender;
amps_t *amps;
@ -1055,10 +1092,8 @@ void call_down_audio(int callref, sample_t *samples, int count)
return;
if (amps->dsp_mode == DSP_MODE_AUDIO_RX_AUDIO_TX) {
sample_t up[(int)((double)count * amps->sender.srstate.factor + 0.5) + 10];
compress_audio(&amps->cstate, samples, count);
count = samplerate_upsample(&amps->sender.srstate, samples, count, up);
jitter_save(&amps->sender.dejitter, up, count);
jitter_save(&amps->sender.dejitter, samples, count, 1, sequence, timestamp, ssrc);
}
}
@ -1071,8 +1106,16 @@ void transaction_timeout(struct timer *timer)
amps_t *amps = trans->amps;
switch (trans->state) {
case TRANS_CALL_MO_ASSIGN_CONFIRM:
case TRANS_CALL_MT_ASSIGN_CONFIRM:
PDEBUG_CHAN(DAMPS, DEBUG_NOTICE, "Timeout after %.0f seconds not receiving initial SAT signal.\n", timer->duration);
PDEBUG_CHAN(DAMPS, DEBUG_INFO, "Release call towards network.\n");
amps_release(amps->trans_list, CAUSE_TEMPFAIL);
break;
case TRANS_CALL:
timeout_sat(amps, timer->duration);
PDEBUG_CHAN(DAMPS, DEBUG_NOTICE, "Timeout after %.0f seconds loosing SAT signal.\n", timer->duration);
PDEBUG_CHAN(DAMPS, DEBUG_INFO, "Release call towards network.\n");
amps_release(amps->trans_list, CAUSE_TEMPFAIL);
break;
case TRANS_CALL_RELEASE:
case TRANS_CALL_RELEASE_SEND:
@ -1080,10 +1123,18 @@ void transaction_timeout(struct timer *timer)
destroy_transaction(trans);
amps_go_idle(amps);
break;
case TRANS_CALL_MT_ALERT:
amps_release(trans, CAUSE_TEMPFAIL);
break;
case TRANS_CALL_MT_ALERT_SEND:
case TRANS_CALL_MT_ALERT_CONFIRM:
if (trans->alert_retry++ == ALERT_TRIES) {
PDEBUG_CHAN(DAMPS, DEBUG_NOTICE, "Phone does not respond to alert order, destroying transaction\n");
amps_release(trans, CAUSE_TEMPFAIL);
} else {
PDEBUG_CHAN(DAMPS, DEBUG_NOTICE, "Phone does not respond to alert order, retrying\n");
trans_new_state(trans, TRANS_CALL_MT_ALERT);
amps_set_dsp_mode(amps, DSP_MODE_AUDIO_RX_FRAME_TX, 0);
}
break;
case TRANS_CALL_MT_ANSWER_WAIT:
PDEBUG_CHAN(DAMPS, DEBUG_NOTICE, "Alerting timeout, destroying transaction\n");
amps_release(trans, CAUSE_NOANSWER);
break;
@ -1173,8 +1224,9 @@ again:
vc = assign_voice_channel(trans);
if (vc) {
PDEBUG_CHAN(DAMPS, DEBUG_INFO, "Assignment complete, voice connected\n");
trans_new_state(trans, TRANS_CALL);
amps_set_dsp_mode(vc, DSP_MODE_AUDIO_RX_AUDIO_TX, 0);
/* timer and other things are processed at assign_voice_channel() */
trans_new_state(trans, TRANS_CALL_MO_ASSIGN_CONFIRM);
amps_set_dsp_mode(vc, DSP_MODE_AUDIO_RX_SILENCE_TX, 0);
}
return NULL;
case TRANS_CALL_MT_ASSIGN:
@ -1184,13 +1236,10 @@ again:
case TRANS_CALL_MT_ASSIGN_SEND:
vc = assign_voice_channel(trans);
if (vc) {
PDEBUG_CHAN(DAMPS, DEBUG_INFO, "Assignment complete, next: sending alerting on VC\n");
trans->chan = 0;
trans->msg_type = 0;
trans->ordq = 0;
trans->order = 1;
trans_new_state(trans, TRANS_CALL_MT_ALERT);
amps_set_dsp_mode(vc, DSP_MODE_AUDIO_RX_FRAME_TX, 0);
PDEBUG_CHAN(DAMPS, DEBUG_INFO, "Assignment complete, waiting for SAT on VC\n");
/* timer and other things are processed at assign_voice_channel() */
trans_new_state(trans, TRANS_CALL_MT_ASSIGN_CONFIRM);
amps_set_dsp_mode(vc, DSP_MODE_AUDIO_RX_SILENCE_TX, 0);
}
return NULL;
case TRANS_PAGE:
@ -1223,8 +1272,25 @@ transaction_t *amps_tx_frame_fvc(amps_t *amps)
PDEBUG_CHAN(DAMPS, DEBUG_INFO, "Release call was sent, continue sending release\n");
return trans;
case TRANS_CALL_MT_ALERT:
PDEBUG_CHAN(DAMPS, DEBUG_INFO, "Sending alerting\n");
trans->chan = 0;
trans->msg_type = 0;
trans->ordq = 0;
// "Alert with caller ID" causes older phones to interrupt the connection for some reason, therefore we don't use order 17 when no caller ID is set
if (amps->send_callerid && trans->alert_retry == 1 && trans->caller_id[0]) {
PDEBUG_CHAN(DAMPS, DEBUG_INFO, "Sending alerting with caller ID\n");
trans->order = 17;
} else {
PDEBUG_CHAN(DAMPS, DEBUG_INFO, "Sending alerting\n");
trans->order = 1;
}
trans_new_state(trans, TRANS_CALL_MT_ALERT_SEND);
return trans;
case TRANS_CALL_MT_ALERT_SEND:
PDEBUG_CHAN(DAMPS, DEBUG_INFO, "Alerting was sent, continue waiting for ST or timeout\n");
timer_start(&trans->timer, ALERT_TO);
amps_set_dsp_mode(amps, DSP_MODE_AUDIO_RX_SILENCE_TX, 0);
trans_new_state(trans, TRANS_CALL_MT_ALERT_CONFIRM);
return NULL;
default:
return NULL;
}

View File

@ -10,6 +10,7 @@ enum dsp_mode {
DSP_MODE_OFF, /* channel not active (VC) */
DSP_MODE_AUDIO_RX_AUDIO_TX, /* stream audio */
DSP_MODE_AUDIO_RX_FRAME_TX, /* stream audio, send frames */
DSP_MODE_AUDIO_RX_SILENCE_TX, /* stream audio, send silence */
DSP_MODE_FRAME_RX_FRAME_TX, /* send and decode frames */
};
@ -56,6 +57,7 @@ struct amps {
/* system info */
amps_si si;
int send_callerid; /* if set, caller ID is transmitted */
/* cell nr selection */
int cell_auto; /* if set, cell_nr is selected automatically */
@ -140,6 +142,12 @@ struct amps {
uint8_t tx_fvc_msg_type; /* message (3 values) */
uint8_t tx_fvc_ordq;
uint8_t tx_fvc_order;
char tx_fvc_callerid[34]; /* caller ID */
int tx_fvc_callerid_present;/* presentation of caller ID */
int tx_fvc_callerid_screen; /* screening of caller ID */
int tx_fvc_callerid_signal; /* signal to send in conjunction with caller ID */
int tx_fvc_word_count; /* counts transmitted words in a muli word message */
int tx_fvc_word_repeat; /* counts repeats of mulit word message */
/* SAT tone */
int sat; /* use SAT tone 0..2 */
int sat_samples; /* number of samples in buffer for supervisory detection */
@ -175,7 +183,7 @@ const char *amps_min12number(uint32_t min1);
void amps_number2min(const char *number, uint32_t *min1, uint16_t *min2);
const char *amps_min2number(uint32_t min1, uint16_t min2);
const char *amps_scm(uint8_t scm);
int amps_create(const char *kanal, enum amps_chan_type chan_type, const char *device, int use_sdr, int samplerate, double rx_gain, double tx_gain, 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, amps_si *si, uint16_t sid, uint8_t sat, int polarity, int tolerant, int loopback);
int amps_create(const char *kanal, enum amps_chan_type chan_type, const char *device, int use_sdr, int samplerate, double rx_gain, double tx_gain, 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, amps_si *si, uint16_t sid, uint8_t sat, int polarity, int send_callerid, int tolerant, int loopback);
void amps_destroy(sender_t *sender);
void amps_go_idle(amps_t *amps);
void amps_rx_signaling_tone(amps_t *amps, int tone, double quality);

View File

@ -5,10 +5,16 @@
#include "outoforder.h"
#include "invalidnumber.h"
#include "congestion.h"
#include "../libmobile/main_mobile.h"
const int tacs = 0;
const int jtacs = 0;
const struct number_lengths number_lengths[] = {
{ 10, "AMPS number (NPA-XXX-XXXX)" },
{ 0, NULL }
};
const char *number_prefixes[] = {
"1xxxxxxxxxx",
"+1xxxxxxxxxx",

View File

@ -679,6 +679,7 @@ static struct amps_stations {
{ 16413, 0, "Saskatoon", "SK", "Cantel, Inc.", "403 266 1300", "GTEDS" },
{ 16415, 0, "St. Catharines", "ON", "Cantel, Inc.", "416 440 1300", "GTEDS" },
{ 16419, 0, "Sudbury", "ON", "Cantel, Inc.", "416 440 1300", "GTEDS" },
{ 16420, 0, "Toronto", "ON", "Cantel, Inc.", "416", ":GTEDS" },
{ 16423, 0, "Newmarket", "ON", "Cantel, Inc.", "416 440 1300", "GTEDS" },
{ 16423, 0, "Toronto", "ON", "Cantel, Inc.", "416", ":GTEDS" },
{ 16425, 0, "Abbotsford", "BC", "Cantel, Inc.", "604 687 1440", "GTEDS" },

View File

@ -125,7 +125,7 @@
#define SIG_LOST_COUNT 4 /* number of measures to loose Signaling Tone */
#define CUT_OFF_HIGHPASS 300.0 /* cut off frequency for high pass filter to remove dc level from sound card / sample */
#define BEST_QUALITY 0.68 /* Best possible RX quality */
#define COMFORT_NOISE 0.02 /* audio level of comfort noise (relative to ISDN level) */
#define COMFORT_NOISE 0.02 /* audio level of comfort noise (relative to speech level) */
static sample_t ramp_up[256], ramp_down[256];
@ -253,6 +253,7 @@ int dsp_init_sender(amps_t *amps, int tolerant)
PDEBUG(DDSP, DEBUG_ERROR, "No memory!\n");
return -ENOMEM;
}
PDEBUG(DDSP, DEBUG_DEBUG, "Sat detection interval is %d ms.\n", amps->sat_samples * 1000 / amps->sender.samplerate);
amps->sat_filter_spl = spl;
/* count SAT tones */
@ -399,8 +400,8 @@ again:
rc = amps_encode_frame_fvc(amps, amps->fsk_tx_frame);
else
rc = amps_encode_frame_focc(amps, amps->fsk_tx_frame);
/* check if we have not bit string (change to tx audio)
* we may not store fsk_tx_buffer_pos, because is was reset on a mode achange */
/* check if we have no bit string (change to tx audio / silence)
* we may not store fsk_tx_buffer_pos, because is was reset on a mode change */
if (rc)
return count;
amps->fsk_tx_frame_pos = 0;
@ -473,7 +474,7 @@ static void sat_encode(amps_t *amps, sample_t *samples, int length)
void sender_send(sender_t *sender, sample_t *samples, uint8_t *power, int length)
{
amps_t *amps = (amps_t *) sender;
int count;
int count, input_num;
again:
switch (amps->dsp_mode) {
@ -483,11 +484,19 @@ again:
break;
case DSP_MODE_AUDIO_RX_AUDIO_TX:
memset(power, 1, length);
jitter_load(&amps->sender.dejitter, samples, length);
input_num = samplerate_upsample_input_num(&sender->srstate, length);
jitter_load(&sender->dejitter, samples, input_num);
samplerate_upsample(&sender->srstate, samples, input_num, samples, length);
/* pre-emphasis */
if (amps->pre_emphasis)
pre_emphasis(&amps->estate, samples, length);
/* encode sat */
/* encode SAT during call */
sat_encode(amps, samples, length);
break;
case DSP_MODE_AUDIO_RX_SILENCE_TX:
memset(power, 1, length);
memset(samples, 0, sizeof(*samples) * length);
/* encode SAT while waiting for alert response or answer */
sat_encode(amps, samples, length);
break;
case DSP_MODE_AUDIO_RX_FRAME_TX:
@ -496,6 +505,7 @@ again:
* stopped, process again for rest of stream. */
count = fsk_frame(amps, samples, length);
memset(power, 1, count);
// no SAT during frame transmission, according to specs
samples += count;
power += count;
length -= count;
@ -831,7 +841,7 @@ static void sat_decode(amps_t *amps, sample_t *samples, int length)
static void sender_receive_audio(amps_t *amps, sample_t *samples, int length)
{
transaction_t *trans = amps->trans_list;
sample_t *spl;
sample_t *spl, s;
int max, pos;
int i;
@ -840,7 +850,10 @@ static void sender_receive_audio(amps_t *amps, sample_t *samples, int length)
spl = amps->sat_filter_spl;
pos = amps->sat_filter_pos;
for (i = 0; i < length; i++) {
/* unmute: use buffer, to delay audio, so we do not miss that chunk when SAT is detected */
s = spl[pos];
spl[pos++] = samples[i];
samples[i] = s;
if (pos == max) {
pos = 0;
sat_decode(amps, spl, max);
@ -894,6 +907,7 @@ void sender_receive(sender_t *sender, sample_t *samples, int length, double __at
break;
case DSP_MODE_AUDIO_RX_AUDIO_TX:
case DSP_MODE_AUDIO_RX_FRAME_TX:
case DSP_MODE_AUDIO_RX_SILENCE_TX:
sender_receive_audio(amps, samples, length);
break;
}
@ -923,13 +937,13 @@ void amps_set_dsp_mode(amps_t *amps, enum dsp_mode mode, int frame_length)
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)) {
&& (mode == DSP_MODE_AUDIO_RX_AUDIO_TX || mode == DSP_MODE_AUDIO_RX_FRAME_TX || mode == DSP_MODE_AUDIO_RX_SILENCE_TX)) {
/* reset SAT detection */
sat_reset(amps, "Change from FOCC to FVC");
PDEBUG_CHAN(DDSP, DEBUG_INFO, "Change mode from FOCC to FVC\n");
}
if (amps->dsp_mode == DSP_MODE_OFF
&& (mode == DSP_MODE_AUDIO_RX_AUDIO_TX || mode == DSP_MODE_AUDIO_RX_FRAME_TX)) {
&& (mode == DSP_MODE_AUDIO_RX_AUDIO_TX || mode == DSP_MODE_AUDIO_RX_FRAME_TX || mode == DSP_MODE_AUDIO_RX_SILENCE_TX)) {
/* reset SAT detection */
sat_reset(amps, "Enable FVC");
PDEBUG_CHAN(DDSP, DEBUG_INFO, "Change mode from OFF to FVC\n");
@ -940,6 +954,8 @@ void amps_set_dsp_mode(amps_t *amps, enum dsp_mode mode, int frame_length)
PDEBUG_CHAN(DDSP, DEBUG_INFO, "Change mode from FVC to OFF\n");
}
PDEBUG_CHAN(DDSP, DEBUG_DEBUG, "Reset FSK frame transmitter, due to setting dsp mode.\n");
amps->dsp_mode = mode;
if (frame_length)
amps->fsk_rx_frame_length = frame_length;

View File

@ -3020,6 +3020,37 @@ static uint64_t amps_encode_mobile_station_control_message_word1_b(uint8_t scc,
return amps_encode_word(&frame, &mobile_station_control_message_word1_b, 1);
}
static uint64_t amps_encode_word2_first_alert_with_info_word(uint8_t rl_w, uint8_t signal, uint8_t cpn_rl, uint8_t pi, uint8_t si)
{
frame_t frame;
memset(&frame, 0, sizeof(frame));
frame.ie[AMPS_IE_T1T2] = 1;
frame.ie[AMPS_IE_RL_W] = rl_w;
frame.ie[AMPS_IE_SIGNAL] = signal;
frame.ie[AMPS_IE_CPN_RL] = cpn_rl;
frame.ie[AMPS_IE_PI] = pi;
frame.ie[AMPS_IE_SI] = si;
return amps_encode_word(&frame, &word2_first_alert_with_info_word, 1);
}
static uint64_t amps_encode_wordn_n_minus_1th_alert_with_info_word(const char *character)
{
frame_t frame;
memset(&frame, 0, sizeof(frame));
frame.ie[AMPS_IE_T1T2] = 1;
if (character[0]) {
frame.ie[AMPS_IE_CHARACTER_1] = character[0];
if (character[1]) {
frame.ie[AMPS_IE_CHARACTER_2] = character[1];
if (character[2])
frame.ie[AMPS_IE_CHARACTER_3] = character[2];
}
}
return amps_encode_word(&frame, &wordn_n_minus_1th_alert_with_info_word, 1);
}
/* decoder function of a word */
static frame_t *amps_decode_word(uint64_t word, struct def_word *w)
{
@ -3346,7 +3377,7 @@ static void amps_encode_focc_bits(uint64_t word_a, uint64_t word_b, char *bits)
memcpy(bits + 0, dotting, 10);
bits[10] = 'i';
strcpy(bits + 11, sync_word);
memcpy(bits + 11, sync_word, 11);
bits[22] = 'i';
k = 23;
for (i = 0; i < 5; i++) {
@ -3366,22 +3397,22 @@ static void amps_encode_focc_bits(uint64_t word_a, uint64_t word_b, char *bits)
if (k != 463)
abort();
bits[463] = '\0';
bits[k] = '\0';
#ifdef BIT_DEBUGGING
if (debuglevel == DEBUG_DEBUG) {
char text[64];
strncpy(text, bits, 23);
text[23] = '\0';
#ifdef BIT_DEBUGGING
PDEBUG(DFRAME, DEBUG_INFO, "TX FOCC: %s\n", text);
for (i = 0; i < 10; i++) {
strncpy(text, bits + 23 + i * 44, 44);
text[44] = '\0';
PDEBUG(DFRAME, DEBUG_DEBUG, " word %c - %s\n", (i & 1) ? 'b' : 'a', text);
}
#endif
}
#endif
}
static void amps_encode_fvc_bits(uint64_t word_a, char *bits)
@ -3398,15 +3429,15 @@ static void amps_encode_fvc_bits(uint64_t word_a, char *bits)
memcpy(bits + k, dotting, 37);
k += 37;
}
strcpy(bits + k, sync_word);
memcpy(bits + k, sync_word, 11);
k += 11;
for (j = 39; j >= 0; j--)
bits[k++] = ((word_a >> j) & 1) + '0';
}
if (k != 1032)
abort();
bits[1032] = '\0';
bits[k] = '\0';
#ifdef BIT_DEBUGGING
if (debuglevel == DEBUG_DEBUG) {
@ -3502,7 +3533,16 @@ int amps_encode_frame_fvc(amps_t *amps, char *bits)
amps->tx_fvc_ordq = trans->ordq;
amps->tx_fvc_order = trans->order;
amps->tx_fvc_chan = trans->chan;
strncpy(amps->tx_fvc_callerid, trans->caller_id, sizeof(amps->tx_fvc_callerid) - 1);
amps->tx_fvc_callerid_signal = 1;
amps->tx_fvc_callerid_screen = 3;
if (trans->caller_id[0])
amps->tx_fvc_callerid_present = 0;
else
amps->tx_fvc_callerid_signal = 1;
amps->tx_fvc_send = 1;
amps->tx_fvc_word_count = 0;
amps->tx_fvc_word_repeat = 0;
}
/* on change of dsp mode */
if (amps->dsp_mode != DSP_MODE_AUDIO_RX_FRAME_TX)
@ -3511,11 +3551,32 @@ int amps_encode_frame_fvc(amps_t *amps, char *bits)
/* send scheduled mobile control message */
if (amps->tx_fvc_send) {
amps->tx_fvc_send = 0;
if (amps->tx_fvc_chan)
word = amps_encode_mobile_station_control_message_word1_b(amps->sat, amps->sat, (amps->si.word2.dtx) ? 1 : 0, 0, 0, amps->si.vmac, amps->tx_fvc_chan);
else
word = amps_encode_mobile_station_control_message_word1_a(amps->sat, amps->tx_fvc_msg_type, amps->tx_fvc_ordq, amps->tx_fvc_order);
if (amps->tx_fvc_word_count == 0) {
if (amps->tx_fvc_chan)
word = amps_encode_mobile_station_control_message_word1_b(amps->sat, amps->sat, (amps->si.word2.dtx) ? 1 : 0, 0, 0, amps->si.vmac, amps->tx_fvc_chan);
else
word = amps_encode_mobile_station_control_message_word1_a(amps->sat, amps->tx_fvc_msg_type, amps->tx_fvc_ordq, amps->tx_fvc_order);
/* done, if we don't have ALERTING with info */
if (amps->tx_fvc_order != 17)
amps->tx_fvc_send = 0;
} else if (amps->tx_fvc_word_count == 1) {
int cpn_rl, rl_w;
/* number of characters */
cpn_rl = strlen(amps->tx_fvc_callerid);
/* number of frames that are required to hold number of characters */
rl_w = (cpn_rl + 2) / 3;
word = amps_encode_word2_first_alert_with_info_word(rl_w, amps->tx_fvc_callerid_signal, cpn_rl, amps->tx_fvc_callerid_present, amps->tx_fvc_callerid_screen);
if (cpn_rl == 0)
amps->tx_fvc_send = 0;
} else {
const char *callerid;
/* chunk of caller ID */
callerid = amps->tx_fvc_callerid + (amps->tx_fvc_word_count - 2) * 3;
word = amps_encode_wordn_n_minus_1th_alert_with_info_word(callerid);
if (strlen(callerid) <= 3)
amps->tx_fvc_send = 0;
}
amps->tx_fvc_word_count++;
} else
return 1;

View File

@ -2,10 +2,16 @@
#include "../amps/main.h"
#include "../amps/tones.h"
#include "../amps/outoforder.h"
#include "../libmobile/main_mobile.h"
const int tacs = 1;
const int jtacs = 1;
const struct number_lengths number_lengths[] = {
{ 10, "JTACS number (440-XXXXXXX)" },
{ 0, NULL }
};
const char *number_prefixes[] = { NULL };
int main(int argc, char *argv[])

View File

@ -38,7 +38,10 @@
int num_chan_type = 0;
enum amps_chan_type chan_type[MAX_SENDER] = { CHAN_TYPE_CC_PC_VC };
const char *flip_polarity = "";
int ms_power = 4, dtx = 0, dcc = 0, scc = 0, sid = 0, regh = 1, regr = 1, pureg = 0, pdreg = 0, locaid = -1, regincr = 300, bis = 0;
int ms_power = 4;
int dtx = 0;
int send_callerid = 0;
int dcc = 0, scc = 0, sid = 0, regh = 1, regr = 1, pureg = 0, pdreg = 0, locaid = -1, regincr = 300, bis = 0;
int tolerant = 0;
void print_help(const char *arg0)
@ -57,12 +60,24 @@ void print_help(const char *arg0)
printf(" If the phone shows 'NoSrv', try the other way.\n");
printf(" -P --ms-power <power level>\n");
printf(" Give power level of the mobile station 0..7. (default = '%d')\n", ms_power);
printf(" 0 = %2d W; 1 = 1.6 W; 2 = 630 mW; 3 = 250 mW;\n", (tacs) ? 10 : 4);
printf(" 4 = 100 mW; 5 = 40 mW; 6 = 16 mW; 7 = 6.3 mW\n");
if (!tacs) {
printf(" 0 = 4 W; 1 = 1.6 W; 2 = 630 mW; 3 = 250 mW;\n");
printf(" 4 = 100 mW; 5 = 40 mW; 6 = 16 mW; 7 = 6.3 mW\n");
} else {
/* tacs, not jtacs: https://www.academia.edu/8265916/Total_Access_Communication_System?email_work_card=view-paper */
printf(" 0 = 2.28 W; 1 = 1.12 W; 2 = 447 mW; 3 = 178 mW;\n");
printf(" 4 = 70.8 mW; 5 = 28.2 mW; 6 = 11.2 mW; 7 = 4.5 mW\n");
}
printf(" -D --dtx <parameter>\n");
printf(" Give DTX parameter for Discontinuous Transmission. (default = '%d')\n", dtx);
printf(" 0 = disable DTX; 1 = reserved;\n");
printf(" 2 = 8 dB attenuation in low state; 3 = transmitter off\n");
printf(" -I --caller-id 1 | 0\n");
printf(" If set, the caller ID is sent while ringing the phone. (default = '%d')\n", send_callerid);
printf(" Note that this does not work as documented in the specs. If the phone\n");
printf(" does not support caller ID, it will abort connection on receiving\n");
printf(" caller ID for some unknown reason. Therefore use caller ID only with\n");
printf(" phones that support it.\n");
if (!tacs) {
printf(" -S --sysinfo sid=<System ID> | sid=list\n");
printf(" Give system ID of cell broadcast\n");
@ -110,6 +125,7 @@ static void add_options(void)
option_add('F', "flip-polarity", 1);
option_add('P', "ms-power", 1);
option_add('D', "dtx", 1);
option_add('I', "caller-id", 1);
option_add('S', "sysinfo", 1);
option_add('O', "tolerant", 0);
}
@ -156,6 +172,9 @@ static int handle_options(int short_option, int argi, char **argv)
if (dtx < 0)
dtx = 0;
break;
case 'I':
send_callerid = atoi(argv[argi]);
break;
case 'S':
p = strchr(argv[argi], '=');
if (!p) {
@ -226,10 +245,7 @@ static int handle_options(int short_option, int argi, char **argv)
return 1;
}
static const struct number_lengths number_lengths[] = {
{ 10, "AMPS number" },
{ 0, NULL }
};
extern const struct number_lengths number_lengths[];
int main_amps_tacs(const char *name, int argc, char *argv[])
{
@ -238,6 +254,10 @@ int main_amps_tacs(const char *name, int argc, char *argv[])
int polarity;
int i;
/* jtacs has only system A, so there are only odd AIDs */
if (jtacs)
sid = 1;
/* override default */
dsp_samplerate = 96000;
@ -267,7 +287,7 @@ int main_amps_tacs(const char *name, int argc, char *argv[])
}
if (!num_kanal) {
printf("No channel (\"Kanal\") is specified, I suggest channel %d.\n\n", (!tacs) ? 333 : 323);
printf("No channel (\"Kanal\") is specified, I suggest channel %d.\n\n", (!tacs) ? 333 : ((!jtacs) ? 323 : 418));
print_help(argv[0]);
return 0;
}
@ -384,7 +404,7 @@ int main_amps_tacs(const char *name, int argc, char *argv[])
amps_si si;
init_sysinfo(&si, ms_power, ms_power, dtx, dcc, sid >> 1, regh, regr, pureg, pdreg, locaid, regincr, bis);
rc = amps_create(kanal[i], chan_type[i], dsp_device[i], use_sdr, dsp_samplerate, rx_gain, tx_gain, do_pre_emphasis, do_de_emphasis, write_rx_wave, write_tx_wave, read_rx_wave, read_tx_wave, &si, sid, scc, polarity, tolerant, loopback);
rc = amps_create(kanal[i], chan_type[i], dsp_device[i], use_sdr, dsp_samplerate, rx_gain, tx_gain, do_pre_emphasis, do_de_emphasis, write_rx_wave, write_tx_wave, read_rx_wave, read_tx_wave, &si, sid, scc, polarity, send_callerid, tolerant, loopback);
if (rc < 0) {
fprintf(stderr, "Failed to create \"Sender\" instance. Quitting!\n");
goto fail;

View File

@ -2,10 +2,16 @@
#include "../amps/main.h"
#include "../amps/tones.h"
#include "../amps/outoforder.h"
#include "../libmobile/main_mobile.h"
const int tacs = 1;
const int jtacs = 0;
const struct number_lengths number_lengths[] = {
{ 10, "TACS number (AREA-XXXXXXX)" },
{ 0, NULL }
};
const char *number_prefixes[] = {
"0xxxxxxxxxx",
"+44xxxxxxxxxx",

View File

@ -37,17 +37,25 @@ static const char *trans_state_name(int state)
case TRANS_REGISTER_ACK_SEND:
return "REGISTER ACK SEND";
case TRANS_CALL_MO_ASSIGN:
return "CALL ASSIGN MOBILE ORIGINATING";
return "MO CALL ASSIGNMENT";
case TRANS_CALL_MO_ASSIGN_SEND:
return "CALL ASSIGN MOBILE ORIGINATING SEND";
return "MO CALL ASSIGNMENT SENDING";
case TRANS_CALL_MO_ASSIGN_CONFIRM:
return "MO CALL ASSIGNMENT WAIT CONFIRM";
case TRANS_CALL_MT_ASSIGN:
return "CALL ASSIGN MOBILE TERMINATING";
return "MT CALL ASSIGNMENT";
case TRANS_CALL_MT_ASSIGN_SEND:
return "CALL ASSIGN MOBILE TERMINATING SEND";
return "MT CALL ASSIGNMENT SENDING";
case TRANS_CALL_MT_ASSIGN_CONFIRM:
return "MT CALL ASSIGNMENT WAIT CONFIRM";
case TRANS_CALL_MT_ALERT:
return "CALL ALERT MOBILE TERMINATING";
return "MT CALL ALERT";
case TRANS_CALL_MT_ALERT_SEND:
return "CALL ALERT MOBILE TERMINATING SEND";
return "MT CALL ALERT SENDING";
case TRANS_CALL_MT_ALERT_CONFIRM:
return "MT CALL ALERT WAIT CONFIRM";
case TRANS_CALL_MT_ANSWER_WAIT:
return "MT CALL ANSWER WAIT";
case TRANS_CALL_REJECT:
return "CALL REJECT";
case TRANS_CALL_REJECT_SEND:
@ -79,11 +87,15 @@ const char *trans_short_state_name(int state)
return "REGISTER";
case TRANS_CALL_MO_ASSIGN:
case TRANS_CALL_MO_ASSIGN_SEND:
case TRANS_CALL_MO_ASSIGN_CONFIRM:
case TRANS_CALL_MT_ASSIGN:
case TRANS_CALL_MT_ASSIGN_SEND:
case TRANS_CALL_MT_ASSIGN_CONFIRM:
return "ASSIGN";
case TRANS_CALL_MT_ALERT:
case TRANS_CALL_MT_ALERT_SEND:
case TRANS_CALL_MT_ALERT_CONFIRM:
case TRANS_CALL_MT_ANSWER_WAIT:
return "ALERT";
case TRANS_CALL_REJECT:
case TRANS_CALL_REJECT_SEND:

View File

@ -5,10 +5,14 @@ enum amps_trans_state {
TRANS_REGISTER_ACK_SEND, /* attach request received, sending ack */
TRANS_CALL_MO_ASSIGN, /* assigning channel, waiting to send */
TRANS_CALL_MO_ASSIGN_SEND, /* assigning channel, sending assignment */
TRANS_CALL_MO_ASSIGN_CONFIRM, /* assignment sent, waiting for confirm (SAT) */
TRANS_CALL_MT_ASSIGN, /* assigning channel, waiting to send */
TRANS_CALL_MT_ASSIGN_SEND, /* assigning channel, sending assignment */
TRANS_CALL_MT_ALERT, /* ringing the phone, sending alert order until signaling tone is received */
TRANS_CALL_MT_ALERT_SEND, /* ringing the phone, signaling tone is received */
TRANS_CALL_MT_ASSIGN_CONFIRM, /* assignment sent, waiting for confirm (SAT) */
TRANS_CALL_MT_ALERT, /* ringing the phone, waiting to send alert */
TRANS_CALL_MT_ALERT_SEND, /* ringing the phone, sending alert */
TRANS_CALL_MT_ALERT_CONFIRM, /* ringing the phone, signaling tone is received */
TRANS_CALL_MT_ANSWER_WAIT, /* ringing the phone, waiting for the phone to answer */
TRANS_CALL_REJECT, /* rejecting channel, waiting to send */
TRANS_CALL_REJECT_SEND, /* rejecting channel, sending reject */
TRANS_CALL, /* active call */
@ -31,6 +35,8 @@ typedef struct transaction {
uint8_t ordq;
uint8_t order;
uint16_t chan; /* channel to assign */
int alert_retry; /* current number of alter order (re)try */
char caller_id[33]; /* id of calling phone */
char dialing[33]; /* number dialed by the phone */
enum amps_trans_state state; /* state of transaction */
struct timer timer; /* for varous timeouts */

View File

@ -513,7 +513,7 @@ void call_down_release(int callref, __attribute__((unused)) int cause)
}
/* Receive audio from call instance. */
void call_down_audio(int callref, sample_t *samples, int count)
void call_down_audio(int callref, uint16_t sequence, uint32_t timestamp, uint32_t ssrc, sample_t *samples, int count)
{
sender_t *sender;
anetz_t *anetz;
@ -526,11 +526,8 @@ void call_down_audio(int callref, sample_t *samples, int count)
if (!sender)
return;
if (anetz->dsp_mode == DSP_MODE_AUDIO) {
sample_t up[(int)((double)count * anetz->sender.srstate.factor + 0.5) + 10];
count = samplerate_upsample(&anetz->sender.srstate, samples, count, up);
jitter_save(&anetz->sender.dejitter, up, count);
}
if (anetz->dsp_mode == DSP_MODE_AUDIO)
jitter_save(&anetz->sender.dejitter, samples, count, 1, sequence, timestamp, ssrc);
}
void call_down_clock(void) {}

View File

@ -357,6 +357,7 @@ static void fsk_tone(anetz_t *anetz, sample_t *samples, int length)
void sender_send(sender_t *sender, sample_t *samples, uint8_t *power, int length)
{
anetz_t *anetz = (anetz_t *) sender;
int input_num;
memset(power, 1, length);
@ -365,7 +366,9 @@ void sender_send(sender_t *sender, sample_t *samples, uint8_t *power, int length
memset(samples, 0, length * sizeof(*samples));
break;
case DSP_MODE_AUDIO:
jitter_load(&anetz->sender.dejitter, samples, length);
input_num = samplerate_upsample_input_num(&sender->srstate, length);
jitter_load(&sender->dejitter, samples, input_num);
samplerate_upsample(&sender->srstate, samples, input_num, samples, length);
break;
case DSP_MODE_TONE:
fsk_tone(anetz, samples, length);

View File

@ -816,7 +816,7 @@ void call_down_release(int callref, int __attribute__((unused)) cause)
}
/* Receive audio from call instance. */
void call_down_audio(int callref, sample_t *samples, int count)
void call_down_audio(int callref, uint16_t sequence, uint32_t timestamp, uint32_t ssrc, sample_t *samples, int count)
{
sender_t *sender;
bnetz_t *bnetz;
@ -831,9 +831,7 @@ void call_down_audio(int callref, sample_t *samples, int count)
if (bnetz->dsp_mode == DSP_MODE_AUDIO
|| bnetz->dsp_mode == DSP_MODE_AUDIO_METER) {
sample_t up[(int)((double)count * bnetz->sender.srstate.factor + 0.5) + 10];
count = samplerate_upsample(&bnetz->sender.srstate, samples, count, up);
jitter_save(&bnetz->sender.dejitter, up, count);
jitter_save(&bnetz->sender.dejitter, samples, count, 1, sequence, timestamp, ssrc);
}
}

View File

@ -361,7 +361,7 @@ static void metering_tone(bnetz_t *bnetz, sample_t *samples, int length)
void sender_send(sender_t *sender, sample_t *samples, uint8_t *power, int length)
{
bnetz_t *bnetz = (bnetz_t *) sender;
int count;
int count, input_num;
memset(power, 1, length);
@ -372,7 +372,9 @@ again:
break;
case DSP_MODE_AUDIO:
case DSP_MODE_AUDIO_METER:
jitter_load(&bnetz->sender.dejitter, samples, length);
input_num = samplerate_upsample_input_num(&sender->srstate, length);
jitter_load(&sender->dejitter, samples, input_num);
samplerate_upsample(&sender->srstate, samples, input_num, samples, length);
if (bnetz->dsp_mode == DSP_MODE_AUDIO_METER)
metering_tone(bnetz, samples, length);
break;
@ -418,7 +420,7 @@ void bnetz_set_dsp_mode(bnetz_t *bnetz, enum dsp_mode mode)
/* reset telegramm */
if (mode == DSP_MODE_TELEGRAMM && bnetz->dsp_mode != mode) {
bnetz->tx_telegramm = 0;
fsk_mod_tx_reset(&bnetz->fsk_mod);
fsk_mod_reset(&bnetz->fsk_mod);
}
PDEBUG_CHAN(DDSP, DEBUG_DEBUG, "DSP mode %s -> %s\n", bnetz_dsp_mode_name(bnetz->dsp_mode), bnetz_dsp_mode_name(mode));

View File

@ -576,7 +576,7 @@ static void cnetz_release(transaction_t *trans, uint8_t cause)
}
/* Receive audio from call instance. */
void call_down_audio(int callref, sample_t *samples, int count)
void call_down_audio(int callref, uint16_t sequence, uint32_t timestamp, uint32_t ssrc, sample_t *samples, int count)
{
sender_t *sender;
cnetz_t *cnetz;
@ -591,7 +591,7 @@ void call_down_audio(int callref, sample_t *samples, int count)
if (cnetz->dsp_mode == DSP_MODE_SPK_V) {
/* store as is, since we convert rate when processing FSK frames */
jitter_save(&cnetz->sender.dejitter, samples, count);
jitter_save(&cnetz->sender.dejitter, samples, count, 1, sequence, timestamp, ssrc);
}
}

View File

@ -158,12 +158,6 @@ int dsp_init_sender(cnetz_t *cnetz, int measure_speed, double clock_speed[2], en
scrambler_setup(&cnetz->scrambler_tx, (double)cnetz->sender.samplerate / 1.1);
scrambler_setup(&cnetz->scrambler_rx, (double)cnetz->sender.samplerate / 1.1);
/* reinit jitter buffer for 8000 kHz */
jitter_destroy(&cnetz->sender.dejitter);
rc = jitter_create(&cnetz->sender.dejitter, 8000 / 5);
if (rc < 0)
goto error;
/* init compandor, according to C-Netz specs, attack and recovery time
* shall not exceed according to ITU G.162 */
init_compandor(&cnetz->cstate, 8000, 5.0, 22.5);
@ -172,7 +166,7 @@ int dsp_init_sender(cnetz_t *cnetz, int measure_speed, double clock_speed[2], en
cnetz->offset_range = ceil(cnetz->fsk_bitduration);
#ifdef TEST_SCRAMBLE
rc = jitter_create(&scrambler_test_jb, cnetz->sender.samplerate / 5);
rc = jitter_create(&scrambler_test_jb, "scramble", cnetz->sender.samplerate, sizeof(sample_t), JITTER_AUDIO);
if (rc < 0) {
PDEBUG_CHAN(DDSP, DEBUG_ERROR, "Failed to init jitter buffer for scrambler test!\n");
exit(0);
@ -577,7 +571,7 @@ void sender_receive(sender_t *sender, sample_t *samples, int length, double rf_l
#ifdef TEST_UNSCRAMBLE
scrambler(&scrambler_test_scrambler1, samples, length);
#endif
jitter_save(&scrambler_test_jb, samples, length);
jitter_save(&scrambler_test_jb, samples, length, 0, 0, 0, 0);
return;
#endif
@ -598,7 +592,8 @@ static int shrink_speech(cnetz_t *cnetz, sample_t *speech_buffer)
/* 1. compress dynamics */
compress_audio(&cnetz->cstate, speech_buffer, 100);
/* 2. upsample */
speech_length = samplerate_upsample(&cnetz->sender.srstate, speech_buffer, 100, speech_buffer);
speech_length = samplerate_upsample_output_num(&cnetz->sender.srstate, 100);
samplerate_upsample(&cnetz->sender.srstate, speech_buffer, 100, speech_buffer, speech_length);
/* 3. scramble */
if (cnetz->scrambler)
scrambler(&cnetz->scrambler_tx, speech_buffer, speech_length);

View File

@ -314,7 +314,7 @@ int send_sto(am791x_t *am791x, sample_t *sample, int length)
/* modulate STO */
phaseshift = am791x->sto_phaseshift65536;
while (count < length && fsk->tx_bitpos < 1.0) {
while (count < length) {
sample[count++] = fsk->sin_tab[(uint16_t)phase];
phase += phaseshift;
if (phase >= 65536.0)

View File

@ -1001,7 +1001,7 @@ static ssize_t dk_ioctl_set(void *inst, int cmd, const void *buf, size_t in_bufs
break;
case TCOON:
PDEBUG(DDATENKLO, DEBUG_DEBUG, "Terminal turns on output.\n");
datenklo->output_off = 1;
datenklo->output_off = 0;
break;
case TCIOFF:
PDEBUG(DDATENKLO, DEBUG_DEBUG, "Terminal turns off input.\n");

View File

@ -6,6 +6,9 @@ bin_PROGRAMS = \
dcf77_SOURCES = \
dcf77.c \
weather.c \
cities.c \
image.c \
main.c
dcf77_LDADD = \
$(COMMON_LA) \

633
src/dcf77/cities.c Normal file
View File

@ -0,0 +1,633 @@
#define _GNU_SOURCE
#include <stdio.h>
#include <string.h>
#include "cities.h"
static struct city_list {
int region;
const char *name;
} city_list[] = {
{ 0, "AGEN" },
{ 0, "AUCH" },
{ 0, "BORDEAUX" },
{ 0, "BRIVE LA GAILLARDE" },
{ 0, "CAHORS" },
{ 0, "MONTAUBAN" },
{ 0, "MONT MARSAN" },
{ 0, "PAU" },
{ 0, "PERIGUEUX" },
{ 0, "TARBES" },
{ 0, "TOULOUSE" },
{ 1, "ANGOULEME" },
{ 1, "LA ROCHELL" },
{ 1, "LA ROCHE S" },
{ 1, "LIMOGES" },
{ 1, "NIORT" },
{ 1, "POITIERS" },
{ 2, "ALENCON" },
{ 2, "AUXERRE" },
{ 2, "BAR LE DUC" },
{ 2, "BLOIS" },
{ 2, "BOBIGNY" },
{ 2, "BOURGES" },
{ 2, "CERGY PONT" },
{ 2, "CHARTRES" },
{ 2, "CRETEIL" },
{ 2, "EVRY" },
{ 2, "LE MANS" },
{ 2, "MELUN" },
{ 2, "NANTERRE" },
{ 2, "NEVERS" },
{ 2, "ORLEANS" },
{ 2, "PARIS" },
{ 2, "REIMS" },
{ 2, "TOURS" },
{ 2, "TROYES" },
{ 2, "VERSAILLES" },
{ 3, "ANGERS" },
{ 3, "BREST" },
{ 3, "CHERBOURG" },
{ 3, "JERSEY" },
{ 3, "LAVAL" },
{ 3, "LORIENT" },
{ 3, "NANTES" },
{ 3, "RENNES" },
{ 3, "ST BRIEUC" },
{ 4, "AURILLAC" },
{ 4, "CLERMON FERRAND" },
{ 4, "FLORAC" },
{ 4, "GUERET" },
{ 4, "MENDE" },
{ 4, "MILLAU" },
{ 4, "MONTLUCON" },
{ 4, "PUY VELAY" },
{ 4, "RODEZ" },
{ 4, "ST-ETIENNE" },
{ 4, "ST FLOUR" },
{ 5, "ALBI" },
{ 5, "BEZIERS" },
{ 5, "CARCASSONN" },
{ 5, "FOIX" },
{ 5, "MONTPELLIER" },
{ 5, "PERPIGNAN" },
{ 6, "AALST" },
{ 6, "ANTWERPEN" },
{ 6, "BOULOGNE" },
{ 6, "BRUGGE" },
{ 6, "BRUSSEL" },
{ 6, "CHARLEROI" },
{ 6, "GENK" },
{ 6, "GENT" },
{ 6, "HALLE" },
{ 6, "HASSELT" },
{ 6, "IXELLES" },
{ 6, "KNOKKE-HEIST" },
{ 6, "KORTRIJK" },
{ 6, "LEUVEN" },
{ 6, "LIEGE" },
{ 6, "LILLE" },
{ 6, "LOKEREN" },
{ 6, "MAASTRICHT" },
{ 6, "MECHELEN" },
{ 6, "MIDDELBURG" },
{ 6, "MONS" },
{ 6, "MOUSCRON" },
{ 6, "NAMUR" },
{ 6, "OOSTENDE" },
{ 6, "ROESELARE" },
{ 6, "SCHAERBEEK" },
{ 6, "TERNEUZEN" },
{ 6, "TOURNAI" },
{ 7, "CHAUMONT" },
{ 7, "DIJON" },
{ 7, "EPINAL" },
{ 7, "LONS LE S" },
{ 7, "METZ" },
{ 7, "NANCY" },
{ 7, "VESOUL" },
{ 8, "ALES" },
{ 8, "AVIGNON" },
{ 8, "MARSEILLE" },
{ 8, "MONTELIMAR" },
{ 8, "NIMES" },
{ 8, "PRIVAS" },
{ 8, "ST TROPEZ" },
{ 8, "TOULON" },
{ 9, "BOURG EN B" },
{ 9, "LYON" },
{ 9, "MACON" },
{ 9, "VALENCE" },
{ 10, "BRIANCON" },
{ 10, "CHAMBERY" },
{ 10, "DIGNE" },
{ 10, "GAP" },
{ 10, "GRENOBLE" },
{ 11, "ANNECY" },
{ 11, "BESANCON" },
{ 11, "DELEMONT" },
{ 11, "LA CHAUX-DE-FONDS" },
{ 12, "ASCHAFFENBURG" },
{ 12, "BAD HOMBURG" },
{ 12, "BAD KREUZNACH" },
{ 12, "DARMSTADT" },
{ 12, "FRANKFURT AM MAIN" },
{ 12, "HEIDELBERG" },
{ 12, "KAISERSLAUTERN" },
{ 12, "KARLSRUHE" },
{ 12, "LANDAU IN DER PFALZ" },
{ 12, "LUDWIGSHAFEN" },
{ 12, "MAINZ" },
{ 12, "MANNHEIM" },
{ 12, "PIRMASENS" },
{ 12, "WORMS" },
{ 13, "BITBURG" },
{ 13, "HAGEN" },
{ 13, "ISERLOHN" },
{ 13, "KOBLENZ" },
{ 13, "LUEDENSCHEID" },
{ 13, "LUXEMBOURG" },
{ 13, "NEUWIED" },
{ 13, "SAARBRUECKEN" },
{ 13, "SEDAN" },
{ 13, "SIEGEN" },
{ 13, "TRIER" },
{ 13, "VERVIERS" },
{ 13, "WIESBADEN" },
{ 14, "AACHEN" },
{ 14, "BIELEFELD" },
{ 14, "BOCHUM" },
{ 14, "BONN" },
{ 14, "DORTMUND" },
{ 14, "DUEREN" },
{ 14, "DUESSELDORF" },
{ 14, "DUISBURG" },
{ 14, "ESSEN" },
{ 14, "GELSENKIRCHEN" },
{ 14, "GUETERSLOH" },
{ 14, "KOELN" },
{ 14, "LINGEN" },
{ 14, "LIPPSTADT" },
{ 14, "MOENCHENGLADBACH" },
{ 14, "MUELHEIM AN DER RUHR" },
{ 14, "MUENSTER" },
{ 14, "NORDHORN" },
{ 14, "OBERHAUSEN" },
{ 14, "OSNABRUECK" },
{ 14, "RECKLINGHAUSEN" },
{ 14, "RHEINE" },
{ 14, "SOLINGEN" },
{ 14, "WESEL" },
{ 14, "WUPPERTAL" },
{ 15, "BRISTOL" },
{ 15, "CARDIFF" },
{ 15, "EXETER" },
{ 15, "HOLYHEAD" },
{ 15, "PLYMOUTH" },
{ 15, "ST DAVIDS" },
{ 15, "SWANSEA" },
{ 16, "BIRMINGHAM" },
{ 16, "BLACKPOOL" },
{ 16, "LEEDS" },
{ 16, "LEICESTER" },
{ 16, "LIVERPOOL" },
{ 16, "MANCHESTER" },
{ 16, "MIDDLESBROUGH" },
{ 16, "NEWCASTLE" },
{ 16, "NOTTINGHAM" },
{ 16, "SHEFFIELD" },
{ 17, "AMIENS" },
{ 17, "BEAUVAIS" },
{ 17, "CAEN" },
{ 17, "EVREUX" },
{ 17, "LAON" },
{ 17, "LE HAVRE" },
{ 17, "ROUEN" },
{ 18, "BOURNEMOUT" },
{ 18, "BRIGHTON" },
{ 18, "CAMBRIDGE" },
{ 18, "DOVER" },
{ 18, "IPSWICH" },
{ 18, "KINGSTON" },
{ 18, "LONDON" },
{ 18, "NORTHAMPTON" },
{ 18, "NORWICH" },
{ 18, "OXFORD" },
{ 18, "PORTSMOUTH" },
{ 18, "READING" },
{ 18, "SOUTHAMPTON" },
{ 19, "BORKUM" },
{ 19, "BREMERHAVEN" },
{ 19, "CUXHAVEN" },
{ 19, "DEN HELDER" },
{ 19, "ELMSHORN" },
{ 19, "EMDEN" },
{ 19, "GRONINGEN" },
{ 19, "HAMBURG" },
{ 19, "LEEUWARDEN" },
{ 19, "NORDERTSTEDT" },
{ 19, "SPIEKEROOG" },
{ 19, "ST PETER ORDING" },
{ 19, "SYLT" },
{ 19, "TEXEL" },
{ 19, "WILHELMSHAVEN" },
{ 20, "ALBORG" },
{ 20, "ESBJERG" },
{ 20, "FREDERIKSHAVN" },
{ 20, "HERNING" },
{ 20, "HOLSTERBRO" },
{ 20, "SKAGEN" },
{ 20, "THISTED" },
{ 20, "THYBOROEN" },
{ 20, "VIBORG" },
{ 21, "ARHUS" },
{ 21, "FREDERICIA" },
{ 21, "HORSENS" },
{ 21, "KOLDING" },
{ 21, "ODENSE" },
{ 21, "RANDERS" },
{ 21, "SILKEBORG" },
{ 21, "VEJLE" },
{ 22, "BRAUNSCHWEIG" },
{ 22, "BREMEN" },
{ 22, "CELLE" },
{ 22, "GOSLAR" },
{ 22, "HAMELN" },
{ 22, "HANNOVER" },
{ 22, "HERFORD" },
{ 22, "HILDESHEIM" },
{ 22, "LUENEBURG" },
{ 22, "MAGDEBURG" },
{ 22, "MINDEN" },
{ 22, "OLDENBURG" },
{ 22, "WOLFSBURG" },
{ 23, "HELSINGOER" },
{ 23, "KALUNDBORG" },
{ 23, "KOEBENHAVN" },
{ 23, "MALMOE" },
{ 23, "NAESTVED" },
{ 23, "ROSKILDE" },
{ 23, "SLAGELSE" },
{ 24, "FEHMARN" },
{ 24, "FLENSBURG" },
{ 24, "GREIFSWALD" },
{ 24, "KIEL" },
{ 24, "LUEBECK" },
{ 24, "ROSTOCK" },
{ 24, "RUEGEN" },
{ 24, "SCHWERIN" },
{ 24, "STRALSUND" },
{ 24, "SZCZECIN" },
{ 24, "WISMAR" },
{ 25, "AUGSBURG" },
{ 25, "DEGGENDORF" },
{ 25, "INGOLSTADT" },
{ 25, "LANDSHUT" },
{ 25, "PASSAU" },
{ 25, "REGENSBURG" },
{ 25, "ULM" },
{ 26, "BURGHAUSEN" },
{ 26, "FRIEDRICHSHAFEN" },
{ 26, "KEMPTEN" },
{ 26, "LINZ" },
{ 26, "MUENCHEN" },
{ 26, "ROSENHEIM" },
{ 26, "SIGMARINGEN" },
{ 26, "WELS" },
{ 27, "BOLZANO" },
{ 27, "MERANO" },
{ 27, "TRENTO" },
{ 28, "ANSBACH" },
{ 28, "BAMBERG" },
{ 28, "BAYREUTH" },
{ 28, "ERLANGEN" },
{ 28, "FUERTH" },
{ 28, "NUERNBERG" },
{ 28, "SCHWEINFURT" },
{ 28, "WEIDEN" },
{ 28, "WERTHEIM" },
{ 28, "WUERZBURG" },
{ 29, "ALTENBURG" },
{ 29, "BAUTZEN" },
{ 29, "CHEMNITZ" },
{ 29, "COTTBUS" },
{ 29, "DESSAU" },
{ 29, "DRESDEN" },
{ 29, "EISENHUETTENSTADT" },
{ 29, "GOERLITZ" },
{ 29, "HALLE" },
{ 29, "HOYERSWERDA" },
{ 29, "LEIPZIG" },
{ 29, "WITTENBERG" },
{ 29, "WROCLAW" },
{ 30, "EISENACH" },
{ 30, "ERFURT" },
{ 30, "GOTHA" },
{ 30, "HOF" },
{ 30, "JENA" },
{ 30, "NORDHAUSEN" },
{ 30, "PLAUEN" },
{ 30, "SUHL" },
{ 30, "WEIMAR" },
{ 30, "ZWICKAU" },
{ 31, "EVIAN" },
{ 31, "FRIBOURG" },
{ 31, "GENEVE" },
{ 31, "LAUSANNE" },
{ 31, "MONTREUX" },
{ 31, "NEUCHATEL" },
{ 32, "AARAU" },
{ 32, "BERN" },
{ 32, "BIENNE" },
{ 32, "FRAUENFELD" },
{ 32, "KONSTANZ" },
{ 32, "LUZERN" },
{ 32, "SCHAFFHAUSEN" },
{ 32, "SOLOTHURN" },
{ 32, "ZUERICH" },
{ 32, "ZUG" },
{ 33, "ADELBODEN" },
{ 33, "GRINDELWALD" },
{ 33, "INTERLAKEN" },
{ 34, "BRIG" },
{ 34, "MARTIGNY" },
{ 34, "SION" },
{ 35, "ALTDORF" },
{ 35, "GLARUS" },
{ 35, "SARNEN" },
{ 35, "SCHWYZ" },
{ 35, "STANS" },
{ 35, "ST.GALLEN" },
{ 36, "CHUR" },
{ 36, "DAVOS" },
{ 37, "FULDA" },
{ 37, "GIESSEN" },
{ 37, "GOETTINGEN" },
{ 37, "KASSEL" },
{ 37, "MARBURG" },
{ 38, "BELLINZONA" },
{ 38, "EDOLO" },
{ 38, "LOCARNO" },
{ 38, "LUGANO" },
{ 39, "AOSTA" },
{ 39, "SESTRIERE" },
{ 40, "ALESSANDRIA" },
{ 40, "BERGAMO" },
{ 40, "BRESCIA" },
{ 40, "MILANO" },
{ 40, "PARMA" },
{ 40, "PIACENZA" },
{ 40, "TORINO" },
{ 40, "VERONA" },
{ 41, "FIRENZE" },
{ 41, "PERUGIA" },
{ 41, "PISA" },
{ 41, "ROMA" },
{ 41, "SIENA" },
{ 41, "VATICANO" },
{ 42, "AMSTERDAM" },
{ 42, "ARNHEM" },
{ 42, "ASSEN" },
{ 42, "DEN HAAG" },
{ 42, "EINDHOVEN" },
{ 42, "HAARLEM" },
{ 42, "LELYSTAD" },
{ 42, "ROTTERDAM" },
{ 42, "S.HERTOGENBOSCH" },
{ 42, "UTRECHT" },
{ 42, "ZWOLLE" },
{ 43, "CANNES" },
{ 43, "GENOVA" },
{ 43, "LA SPEZIA" },
{ 43, "MONACO" },
{ 43, "NICE" },
{ 43, "SAN REMO" },
{ 44, "BOLOGNA" },
{ 44, "NOVA GORIC" },
{ 44, "RIJEKA" },
{ 44, "RIMINI" },
{ 44, "TRIESTE" },
{ 44, "UDINE" },
{ 44, "VENEZIA" },
{ 45, "BASEL" },
{ 45, "BELFORT" },
{ 45, "COLMAR" },
{ 45, "FREIBURG" },
{ 45, "LIESTAL" },
{ 45, "LOERRACH" },
{ 45, "MULHOUSE" },
{ 45, "OFFENBURG" },
{ 45, "STRASBOURG" },
{ 46, "GRAZ" },
{ 46, "KLAGENFURT" },
{ 46, "LIENZ" },
{ 46, "LJUBLJANA" },
{ 46, "MARIBOR" },
{ 46, "SEMMERING" },
{ 46, "VILLACH" },
{ 46, "ZELTWEG" },
{ 47, "BAD GASTEIN" },
{ 47, "INNSBRUCK" },
{ 47, "ISCHGL" },
{ 47, "LANDECK" },
{ 47, "SOELDEN" },
{ 47, "TUXERTAL" },
{ 48, "BAD TOELZ" },
{ 48, "BERCHTESGADEN" },
{ 48, "BISCHOFSHOFEN" },
{ 48, "BREGENZ" },
{ 48, "GARMISCH PARTENKIRCHEN" },
{ 48, "KITZBUEHEL" },
{ 48, "LINDAU" },
{ 48, "SALZBURG" },
{ 48, "SCHLADMING" },
{ 48, "STEYR" },
{ 48, "VADUZ" },
{ 49, "BRATISLAVA" },
{ 49, "EISENSTADT" },
{ 49, "GYOER" },
{ 49, "KLOSTERNEUBURG" },
{ 49, "SOPRON" },
{ 49, "SZOMBATHELY" },
{ 49, "TRENCIN" },
{ 49, "WIEN" },
{ 49, "WIENER NEUSTADT" },
{ 49, "ZALAEGERSZEG" },
{ 50, "BRNO" },
{ 50, "BUDEJOVICE" },
{ 50, "CHEB" },
{ 50, "HAVLICKAV BROD" },
{ 50, "HRADEC/KRA" },
{ 50, "JIHLAVA" },
{ 50, "KARLOVY VARY" },
{ 50, "KLADNO" },
{ 50, "MOST" },
{ 50, "OLOMOUC" },
{ 50, "OSTRAVA" },
{ 50, "PARDUBICE" },
{ 50, "PLZEN" },
{ 50, "PRAHA" },
{ 50, "PREROV" },
{ 50, "PROSTEJOV" },
{ 50, "ST POELTEN" },
{ 50, "ZWETTL" },
{ 51, "CHOMUTOV" },
{ 51, "DECIN" },
{ 51, "LIBEREC" },
{ 51, "OPAVA" },
{ 51, "PIRNA" },
{ 51, "SNIEZKA" },
{ 51, "TEPLICE" },
{ 51, "USTI NAD LABEM" },
{ 51, "WALBRZYCH" },
{ 52, "BERLIN" },
{ 52, "BRANDENBURG" },
{ 52, "FINOW" },
{ 52, "FRANKFURT AN DER ODER" },
{ 52, "GORZOW" },
{ 52, "NEUBRANDENBURG" },
{ 52, "POTSDAM" },
{ 52, "POZNAN" },
{ 52, "STENDAL" },
{ 53, "GOETEBORG" },
{ 53, "HALMSTAD" },
{ 54, "GAEVLE" },
{ 54, "NYKOPING" },
{ 54, "STOCKHOLM" },
{ 54, "UPPSALA" },
{ 54, "VAESTERAS" },
{ 55, "BORGHOLM" },
{ 55, "BORNHOLM" },
{ 55, "KALMAR" },
{ 55, "LINKOEPING" },
{ 55, "RONNE" },
{ 55, "VISBY" },
{ 56, "BORAS" },
{ 56, "JOENKOEPING" },
{ 56, "KARLSTAD" },
{ 56, "OEREBRO" },
{ 57, "BADEN-BADEN" },
{ 57, "DONAUESCHINGEN" },
{ 57, "FREUDENSTADT" },
{ 57, "VILLINGEN-SCHWENNINGEN" },
{ 58, "DRAMMEN" },
{ 58, "FREDRIKSTADEN" },
{ 58, "OSLO" },
{ 58, "TOENSBERG" },
{ 59, "AALEN" },
{ 59, "GOEPPINGEN" },
{ 59, "HEILBRONN" },
{ 59, "PFORZHEIM" },
{ 59, "REUTLINGEN" },
{ 59, "SCHWAEBISCH GMUEND" },
{ 59, "STUTTGART" },
{ 59, "TUEBINGEN" },
{ 60, "NAPOLI" },
{ 61, "ANCONA" },
{ 61, "PESCARA" },
{ 61, "SAN MARINO" },
{ 62, "BARI" },
{ 62, "FOGGIA" },
{ 62, "LECCE" },
{ 63, "BEKESCSABA" },
{ 63, "BRANSKA" },
{ 63, "BUDAPEST" },
{ 63, "DEBRECEN" },
{ 63, "DUNAIJVAROS" },
{ 63, "EGER" },
{ 63, "ERD" },
{ 63, "HODMEZOVASARHELY" },
{ 63, "KAPOSVAR" },
{ 63, "KECSKEMET" },
{ 63, "KOSICE" },
{ 63, "MISKOLC" },
{ 63, "NAGYKANIZSA" },
{ 63, "NYIREGYHAZA" },
{ 63, "OZD" },
{ 63, "PECS" },
{ 63, "SALGOTARJAN" },
{ 63, "SIOFOK" },
{ 63, "SZEGED" },
{ 63, "SZEKESFEEVAR" },
{ 63, "SZOLNOK" },
{ 63, "TATABANYA" },
{ 63, "VESZPREM" },
{ 64, "MADRID" },
{ 65, "BILBAO" },
{ 66, "CATANIA" },
{ 66, "COSENZA" },
{ 66, "MESSINA" },
{ 66, "PALERMO" },
{ 66, "REGGIO CALABRIA" },
{ 67, "IBIZA" },
{ 67, "MAHON" },
{ 67, "PALMA DE MALLORCA" },
{ 68, "VALENCIA" },
{ 69, "BARCELONA" },
{ 69, "FIGUERES" },
{ 69, "GIRONA" },
{ 69, "LLORET DE MAR" },
{ 70, "ANDORRA LA VELLA" },
{ 71, "SEVILLA" },
{ 72, "LISBOA" },
{ 73, "AJACCIO" },
{ 73, "BASTIA" },
{ 73, "CAGLIARI" },
{ 73, "SASSARI" },
{ 74, "GIJON" },
{ 75, "CORK" },
{ 75, "GALWAY" },
{ 75, "LIMERICK" },
{ 76, "BELFAST" },
{ 76, "DUBLIN" },
{ 77, "ABERDEEN" },
{ 77, "EDINBURGH" },
{ 77, "GLASGOW" },
{ 77, "ISLE OF MAN" },
{ 78, "BERGEN" },
{ 78, "STAVANGER" },
{ 79, "TRONDHEIM" },
{ 80, "SUNDSVALL" },
{ 81, "GDANSK" },
{ 81, "OLSZTYN" },
{ 81, "SLUPSK" },
{ 82, "BIALYSTOK" },
{ 82, "BYDGOSZCZ" },
{ 82, "LODZ" },
{ 82, "LUBLIN" },
{ 82, "TORUN" },
{ 82, "WARSZAWA" },
{ 83, "BIELSKO" },
{ 83, "KATOWICE" },
{ 83, "KIELCE" },
{ 83, "KRAKOW" },
{ 83, "OPOLE" },
{ 83, "RZESZOW" },
{ 83, "ZAKOPANE" },
{ 84, "UMEA" },
{ 85, "FALUN" },
{ 85, "OESTERSUND" },
{ 86, "SAMEDAN" },
{ 87, "OSIJEK" },
{ 87, "ZAGREB" },
{ 88, "ZERMATT" },
{ 89, "SPLIT" },
{ 24, "Doerphof" },
{ -1, NULL }
};
void display_city(const char *search)
{
int i;
int found = 0;
for (i = 0; city_list[i].name; i++) {
if (strcasestr(city_list[i].name, search)) {
found++;
printf("City %s is located in region %d.\n", city_list[i].name, city_list[i].region);
}
}
if (!found) {
printf("No city found for '%s', try larger city.\n", search);
}
}

3
src/dcf77/cities.h Normal file
View File

@ -0,0 +1,3 @@
void display_city(const char *search);

View File

@ -1,5 +1,5 @@
/* implementation of DCF77 transmitter and receiver
/* implementation of DCF77 transmitter and receiver, including weather
*
* (C) 2022 by Andreas Eversberg <jolly@eversberg.eu>
* All Rights Reserved
@ -28,6 +28,7 @@
#include <math.h>
#include "../libdebug/debug.h"
#include "dcf77.h"
#include "weather.h"
double get_time(void);
@ -35,7 +36,6 @@ double get_time(void);
#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
@ -43,6 +43,9 @@ double get_time(void);
#define level2db(level) (20 * log10(level))
/* uncomment to speed up transmission by factor 10 and feed the data to the receiver */
//#define DEBUG_LOOP
static int fast_math = 0;
static float *sin_tab = NULL, *cos_tab = NULL;
@ -50,6 +53,292 @@ 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" };
const char *datasets_0_59[8] = {
"Maximum values 1st day (today)",
"Minimum values 1st day (today)",
"Maximum values 2nd day (tomorrow)",
"Minimum values 2nd day (tomorrow)",
"Maximum values 3rd day (in two days)",
"Minimum values 3rd day (in two days)",
"Maximum values 4th day (in three days)",
"Wind and weather anomaly 4th day (in three days)",
};
const char *datasets_60_89[2] = {
"Maximum values 1st day (following day)",
"Maximum values 2nd day (2nd following day)",
};
const char *region[90] = {
"F - Bordeaux, Aquitaine (Suedwestfrankreich)",
"F - La Rochelle, Poitou-Charentes (Westkueste Frankreichs)",
"F - Paris, Ile-de-France (Pariser Becken)",
"F - Brest, Bretagne",
"F - Clermont-Ferrand (Massif Central), Auvergne (Zentralmassiv)",
"F - Beziers, Languedoc-Roussillon",
"B - Bruxelles, Brussel (Benelux)",
"F - Dijon (Bourgogne), Bourgogne (Ostfrankreich / Burgund)",
"F - Marseille, Provence-Alpes-Côte d'Azur",
"F - Lyon (Rhone-Alpes), Rhone-Alpes (Rhonetal)",
"F - Grenoble (Savoie), Rhone-Alpes (Franz. Alpen)",
"CH - La Chaux de Fond, Jura",
"D - Frankfurt am Main, Hessen (Unterer Rheingraben)",
"D - Trier, Westliches Mittelgebirge",
"D - Duisburg, Nordrhein-Westfalen",
"GB - Swansea, Wales (Westl. England / Wales)",
"GB - Manchester, England (Noerdliches England)",
"F - le Havre, Haute-Normandie (Normandie)",
"GB - London, England (Suedostengland / London)",
"D - Bremerhaven, Bremen (Nordseekueste)",
"DK - Herning, Ringkobing (Nordwestliches Juetland)",
"DK - Arhus, Arhus (Oestliches Juetland)",
"D - Hannover, Niedersachsen (Norddeutschland)",
"DK - Kobenhavn, Staden Kobenhaven (Seeland)",
"D - Rostock, Mecklenburg-Vorpommern (Ostseekueste)",
"D - Ingolstadt, Bayern (Donautal)",
"D - Muenchen, Bayern (Suedbayern)",
"I - Bolzano, Trentino-Alto Adige (Suedtirol)",
"D - Nuernberg, Bayern (Nordbayern)",
"D - Leipzig, Sachsen",
"D - Erfurt, Thueringen",
"CH - Lausanne, Genferseeregion (Westl. Schweizer Mitteland)",
"CH - Zuerich (Oestl. Schweizer Mittelland)",
"CH - Adelboden (Westl. Schweizer Alpennordhang)",
"CH - Sion, Wallis",
"CH - Glarus, Oestlicher Schweizer Alpennordhang",
"CH - Davos, Graubuenden",
"D - Kassel, Hessen (Mittelgebirge Ost)",
"CH - Locarno, Tessin",
"I - Sestriere, Piemont. Alpen",
"I - Milano, Lombardia (Poebene)",
"I - Roma, Lazio (Toskana)",
"NL - Amsterdam, Noord-Holland (Holland)",
"I - Genova, Liguria (Golf von Genua)",
"I - Venezia, Veneto (Pomuendung)",
"F - Strasbourg, Alsace (Oberer Rheingraben)",
"A - Klagenfurt, Kaernten (Oesterreich. Alpensuedhang)",
"A - Innsbruck, Tirol (Inneralpine Gebiete Oesterreichs)",
"A - Salzburg, Bayr. / Oesterreich. Alpennordhang",
"SK (Oesterreich / Slovakia) - Wien / Bratislava",
"CZ - Praha, Prag (Tschechisches Becken)",
"CZ - Decin, Severocesky (Erzgebirge)",
"D - Berlin, Ostdeutschland",
"S - Goeteborg, Goeteborgs och Bohus Laen (Westkueste Schweden)",
"S - Stockholm, Stockholms Laen (Stockholm)",
"S - Kalmar, Kalmar Laen (Schwedische Ostseekueste)",
"S - Joenkoeping, Joenkoepings Laen (Suedschweden)",
"D - Donaueschingen, Baden-Wuerttemberg (Schwarzwald / Schwaebische Alb)",
"N - Oslo",
"D - Stuttgart, Baden-Wuerttemberg (Noerdl. Baden Wuerttemberg)",
"I - Napoli",
"I - Ancona",
"I - Bari",
"HU - Budapest",
"E - Madrid",
"E - Bilbao",
"I - Palermo",
"E - Palma de Mallorca",
"E - Valencia",
"E - Barcelona",
"AND - Andorra",
"E - Sevilla",
"P - Lissabon",
"I - Sassari, (Sardinien / Korsika)",
"E - Gijon",
"IRL - Galway",
"IRL - Dublin",
"GB - Glasgow",
"N - Stavanger",
"N - Trondheim",
"S - Sundsvall",
"PL - Gdansk",
"PL - Warszawa",
"PL - Krakow",
"S - Umea",
"S - Oestersund",
"CH - Samedan",
"CR - Zagreb",
"CH - Zermatt",
"CR - Split",
};
const char *weathers_day[16] = {
"Reserved",
"Sunny",
"Partly clouded",
"Mostly clouded",
"Overcast",
"Heat storms",
"Heavy Rain",
"Snow",
"Fog",
"Sleet",
"Rain shower",
"Light rain",
"Snow showers",
"Frontal storms",
"Stratus cloud",
"Sleet storms",
};
const char *weathers_night[16] = {
"Reserved",
"Clear",
"Partly clouded",
"Mostly clouded",
"Overcast",
"Heat storms",
"Heavy Rain",
"Snow",
"Fog",
"Sleet",
"Rain shower",
"Light rain",
"Snow showers",
"Frontal storms",
"Stratus cloud",
"Sleet storms",
};
const char *extremeweathers[16] = {
"None",
"Heavy Weather 24 hrs.",
"Heavy weather Day",
"Heavy weather Night",
"Storm 24hrs.",
"Storm Day",
"Storm Night",
"Wind gusts Day",
"Wind gusts Night",
"Icy rain morning",
"Icy rain evening",
"Icy rain night",
"Fine dust",
"Ozon",
"Radiation",
"High water",
};
const char *probabilities[8] = {
"0 %",
"15 %",
"30 %",
"45 %",
"60 %",
"75 %",
"90 %",
"100 %",
};
const char *winddirections[16] = {
"North",
"Northeast",
"East",
"Southeast",
"South",
"Southwest",
"West",
"Northwest",
"Changeable",
"Foen",
"Biese N/O",
"Mistral N",
"Scirocco S",
"Tramont W",
"reserved",
"reserved",
};
const char *windstrengths[8] = {
"0",
"0-2",
"3-4",
"5-6",
"7",
"8",
"9",
">=10",
};
const char *yesno[2] = {
"No",
"Yes",
};
const char *anomaly1[4] = {
"Same Weather",
"Jump 1",
"Jump 2",
"Jump 3",
};
const char *anomaly2[4] = {
"0-2 hrs",
"2-4 hrs",
"5-6 hrs",
"7-8 hrs",
};
/* show a list of weather data values */
void list_weather(void)
{
time_t timestamp, t;
struct tm *tm;
int i, j;
/* get time stamp of this day, but at 22:00 UTC */
timestamp = floor(get_time());
timestamp -= timestamp % 86400;
timestamp += 79200;
printf("\n");
printf("List of all regions\n");
printf("-------------------\n");
for (i = 0; i < 90; i++) {
printf("Region: %2d = %s\n", i, region[i]);
for (j = 0; j < 8; j++) {
/* get local time where transmission starts */
if (i < 60) {
t = timestamp + 180 * i + 10800 * j;
tm = localtime(&t);
printf(" -> Transmission at %02d:%02d of %s\n", tm->tm_hour, tm->tm_min, datasets_0_59[j]);
} else if (j < 2) {
t = timestamp + 180 * (i - 60) + 10800 * 7 + 5400 * j;
tm = localtime(&t);
printf(" -> Transmission at %02d:%02d of %s\n", tm->tm_hour, tm->tm_min, datasets_60_89[j]);
}
}
}
printf("\n");
printf("List of all weathers\n");
printf("--------------------\n");
for (i = 0; i < 16; i++) {
if (i == 1)
printf("Weather: %2d = %s (day) %s (night)\n", i, weathers_day[i], weathers_night[i]);
else
printf("Weather: %2d = %s (day and night)\n", i, weathers_day[i]);
}
printf("\n");
printf("List of all extreme weathers\n");
printf("----------------------------\n");
for (i = 1; i < 16; i++) {
printf("Extreme: %2d = %s\n", i, extremeweathers[i]);
}
printf("\n");
}
static const char *show_bits(uint64_t value, int bits)
{
static char bit[128];
int i;
for (i = 0; i < bits; i++)
bit[i] = '0' + ((value >> i) & 1);
sprintf(bit + i, "(%" PRIu64 ")", value);
return bit;
}
/* global init */
int dcf77_init(int _fast_math)
{
@ -82,6 +371,7 @@ void dcf77_exit(void)
}
}
/* instance creation */
dcf77_t *dcf77_create(int samplerate, int use_tx, int use_rx, int test_tone)
{
dcf77_t *dcf77 = NULL;
@ -150,6 +440,7 @@ dcf77_t *dcf77_create(int samplerate, int use_tx, int use_rx, int test_tone)
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);
dcf77->dmp_current_second = display_measurements_add(&dcf77->dispmeas, "Current Second", "%.0f", DISPLAY_MEAS_LAST, DISPLAY_MEAS_LEFT, 0.0, 59.0, -INFINITY);
}
if (tx->enable)
@ -157,9 +448,17 @@ dcf77_t *dcf77_create(int samplerate, int use_tx, int use_rx, int test_tone)
if (rx->enable)
PDEBUG(DDCF77, DEBUG_INFO, "DCF77 receiver has been created.\n");
#if 0
void rx_frame_test(dcf77_t *dcf77, const char *string);
rx_frame_test(dcf77, "00001000011111100100101101001010000110000110011100001010000");
rx_frame_test(dcf77, "00101010111111000100111101000010000110000110011100001010000");
rx_frame_test(dcf77, "00011000101111000100100011000010000110000110011100001010000");
#endif
return dcf77;
}
/* instance destruction */
void dcf77_destroy(dcf77_t *dcf77)
{
if (dcf77) {
@ -171,33 +470,264 @@ void dcf77_destroy(dcf77_t *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)
static void display_weather_temperature(const char *desc, uint32_t weather)
{
int value;
value = (weather >> 16) & 0x3f;
switch (value) {
case 0:
printf("%s%s = < -21 degrees C\n", desc, show_bits(value, 6));
break;
case 63:
printf("%s%s = > 40 degrees C\n", desc, show_bits(value, 6));
break;
default:
printf("%s%s = %d degrees C\n", desc, show_bits(value, 6), value - 22);
}
}
/*
* TX part
*/
/* Adjust given time stamp to the time stamp when the weather of the given
* region istransmitted. An offset is used to start several minutes before
* the transmission of the region starts, so the receiver has time to sync
* to the signal first, so it will not miss that weather info.
*
* Note that this will only set the start of transmitting weather of the
* current day (and day temperature).
*/
time_t dcf77_start_weather(time_t timestamp, int region, int offset)
{
int hour, min;
/* hour+min at UTC */
if (region < 60) {
/* first dataset starts at 22:00 UTC */
hour = (22 + region / 20) % 24;
} else {
/* seventh dataset starts at 19:00 UTC */
hour = (19 + (region - 60) / 20) % 24;
}
min = (region % 20) * 3;
PDEBUG(DDCF77, DEBUG_INFO, "Setting UTC time for region %d to %02d:%02d minutes.\n", region, hour, min);
/* reset to 0:00 UTC at same day */
timestamp -= (timestamp % 86400);
/* add time to start */
timestamp += hour * 3600 + min * 60;
/* substract offset */
PDEBUG(DDCF77, DEBUG_INFO, "Setting timestamp offset to %d minutes.\n", offset);
timestamp -= 60 * offset;
return timestamp;
}
/* set weather to transmit on all regions */
void dcf77_set_weather(dcf77_t *dcf77, int weather_day, int weather_night, int extreme, int rain, int wind_dir, int wind_bft, int temperature_day, int temperature_night)
{
dcf77_tx_t *tx = &dcf77->tx;
double now;
time_t t;
/* get time stamp */
if (timestamp < 0)
now = get_time();
tx->weather = 1;
tx->weather_day = weather_day;
tx->weather_night = weather_night;
tx->rain = rain;
tx->extreme = extreme;
tx->wind_dir = wind_dir;
tx->wind_bft = wind_bft;
tx->temperature_day = temperature_day;
tx->temperature_night = temperature_night;
}
/* generate weather frame from weather data */
static uint64_t generate_weather(time_t timestamp, int minute, int utc_hour, int weather_day, int weather_night, int extreme, int rain, int wind_dir, int wind_bft, int temperature_day, int temperature_night)
{
int dataset = ((utc_hour + 2) % 24) * 20 + (minute / 3); /* data sets since 22:00 UTC */
struct tm *tm;
uint64_t key;
uint32_t weather;
int value, temperature;
int i;
int best, diff;
/* generate key from time stamp of next minute */
timestamp += 60;
tm = localtime(&timestamp);
key = 0;
key |= (uint64_t)(tm->tm_min % 10) << 0;
key |= (uint64_t)(tm->tm_min / 10) << 4;
key |= (uint64_t)(tm->tm_hour % 10) << 8;
key |= (uint64_t)(tm->tm_hour / 10) << 12;
key |= (uint64_t)(tm->tm_mday % 10) << 16;
key |= (uint64_t)(tm->tm_mday / 10) << 20;
key |= (uint64_t)((tm->tm_mon + 1) % 10) << 24;
key |= (uint64_t)((tm->tm_mon + 1) / 10) << 28;
if (tm->tm_wday > 0)
key |= (uint64_t)(tm->tm_wday) << 29;
else
now = timestamp;
t = floor(now);
key |= (uint64_t)(7) << 29;
key |= (uint64_t)(tm->tm_year % 10) << 32;
key |= (uint64_t)((tm->tm_year / 10) % 10) << 36;
/* generate weather data */
timestamp -= 120;
weather = 0;
PDEBUG(DFRAME, DEBUG_INFO, "Encoding weather for dataset %d/480\n", dataset);
printf("Peparing Weather INFO\n");
printf("---------------------\n");
printf("Time (UTC): %02d:%02d\n", (int)(timestamp / 3600) % 24, (int)(timestamp / 60) % 60);
/* dataset and region for 0..59 */
printf("Dataset: %s\n", datasets_0_59[dataset / 60]);
value = dataset % 60;
printf("Region: %d = %s\n", value, region[value]);
/* calc. weather of region */
if (weather_day < 0 || weather_day > 15)
weather_day = 1;
weather |= weather_day << 0;
if (weather_night < 0 || weather_night > 15)
weather_night = 1;
weather |= weather_night << 4;
/* calc. temperature of region 0..59 (day/night) or region 60..89 (day) */
if (((dataset / 60) & 1) == 0 || (dataset / 60) == 7)
temperature = temperature_day + 22;
else
temperature = temperature_night + 22;
if (temperature < 0)
temperature = 0;
if (temperature > 63)
value = 63;
weather |= temperature << 16;
/* show weather of region 0..59 */
if ((dataset / 60) < 7) {
printf("Weather (day): %s = %s\n", show_bits(weather_day, 4), weathers_day[weather_day]);
printf("Weather (night): %s = %s\n", show_bits(weather_night, 4), weathers_night[weather_night]);
}
/* show extreme/wind/rain of region 0..59 */
if (((dataset / 60) & 1) == 0) {
/* even datasets, this is 'Day' data */
if (extreme < 0 || extreme > 15)
value = 0;
else
value = extreme;
printf("Extreme weather: %s = %s\n", show_bits(value, 4), extremeweathers[value]);
weather |= value << 8;
best = 0;
for (i = 0; i < 8; i++) {
diff = abs(atoi(probabilities[i]) - rain);
if (i == 0 || diff < best) {
best = diff;
value = i;
}
}
printf("Rain Probability: %s = %s (best match for %d)\n", show_bits(value, 3), probabilities[value], rain);
weather |= value << 12;
value = 0;
printf("Anomaly: %s = %s\n", show_bits(value, 1), yesno[value]);
weather |= value << 15;
display_weather_temperature("Temperature (day): ", weather);
} else {
/* odd datasets, this is 'Night' data */
if (wind_dir < 0 || wind_dir > 15)
value = 8;
else
value = wind_dir;
printf("Wind direction: %s = %s\n", show_bits(value, 4), winddirections[value]);
weather |= value << 8;
if (wind_bft < 1)
value = 0;
else
if (wind_bft < 7)
value = (wind_bft + 1) / 2;
else
if (wind_bft < 10)
value = wind_bft - 3;
else
value = 7;
printf("Wind strength: %s = %s (best match for %d)\n", show_bits(value, 3), windstrengths[value], wind_bft);
weather |= value << 12;
value = 0;
printf("Anomaly: %s = %s\n", show_bits(value, 1), yesno[value]);
weather |= value << 15;
value = temperature_night + 22;
if (value < 0)
value = 0;
if (value > 63)
value = 63;
weather |= value << 16;
if ((dataset / 60) < 7)
display_weather_temperature("Temperature (night): ", weather);
}
/* show weather and temperature of of region 60..89 */
if ((dataset / 60) == 7) {
printf("Dataset: %s\n", 60 + datasets_60_89[(dataset % 60) / 30]);
value = 60 + (dataset % 30);
printf("Region: %d = %s\n", value, region[value]);
printf("Weather (day): %s = %s\n", show_bits(weather_day, 4), weathers_day[weather_day]);
printf("Weather (night): %s = %s\n", show_bits(weather_night, 4), weathers_night[weather_night]);
display_weather_temperature("Temperature: ", weather);
}
/* the magic '10' bit string */
weather |= 0x1 << 22;
/* encode */
return weather_encode(weather, key);
}
/* transmit chunk of weather data for each minute */
static uint16_t tx_weather(dcf77_tx_t *tx, time_t timestamp, int minute, int hour, int zone)
{
int index = (minute + 2) % 3;
int utc_hour;
uint16_t chunk;
if (index == 0) {
/* convert hour to UTC */
utc_hour = hour - 1;
if (zone & 1)
utc_hour--;
if (utc_hour < 0)
utc_hour += 24;
/* in index 0 we transmit minute + 1 (next minute), so we substract 1 */
tx->weather_cipher = generate_weather(timestamp, (minute + 59) % 60, utc_hour, tx->weather_day, tx->weather_night, tx->extreme, tx->rain, tx->wind_dir, tx->wind_bft, tx->temperature_day, tx->temperature_night);
PDEBUG(DFRAME, DEBUG_INFO, "Transmitting first chunk of weather info.\n");
chunk = (tx->weather_cipher & 0x3f) << 1; /* bit 2-7 */
chunk |= (tx->weather_cipher & 0x0fc0) << 2; /* bit 9-14 */
tx->weather_cipher >>= 12;
return chunk;
}
PDEBUG(DFRAME, DEBUG_INFO, "Transmitting %s chunk of weather info.\n", (index == 1) ? "second" : "third");
chunk = tx->weather_cipher & 0x3fff;
tx->weather_cipher >>= 14;
return chunk;
}
/* set inital time stamp at the moment the stream starts */
void dcf77_tx_start(dcf77_t *dcf77, time_t timestamp, double sub_sec)
{
dcf77_tx_t *tx = &dcf77->tx;
/* current second within minute */
tx->second = t % 60;
tx->second = timestamp % 60;
/* time stamp of next minute */
tx->timestamp = t - tx->second + 60;
tx->timestamp = timestamp - tx->second + 60;
/* wave within current second */
tx->wave = floor(fmod(now, 1.0) * (double)tx->waves_sec);
tx->wave = sub_sec * (double)tx->waves_sec;
/* silence until next second begins */
tx->symbol = 'm'; tx->level = 0;
}
/* transmit one symbol = one second */
static char tx_symbol(dcf77_t *dcf77, time_t timestamp, int second)
{
dcf77_tx_t *tx = &dcf77->tx;
char symbol;
int i, j;
/* generate frame */
if (second == 0 || !tx->data_frame) {
@ -205,18 +735,19 @@ static char tx_symbol(dcf77_t *dcf77, time_t timestamp, int second)
int isdst_next_hour, wday, zone;
uint64_t frame = 0, p;
/* get DST next hour */
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;
/* get weather data */
if (tx->weather) {
frame |= tx_weather(tx, timestamp, tm->tm_min, tm->tm_hour, (tm->tm_isdst > 0) ? 1 : 2) << 1;
/* calling tx_weather() destroys tm, because it is a pointer to a global variable. now we fix it */
tm = localtime(&timestamp);
}
if (tm->tm_wday > 0)
wday = tm->tm_wday;
@ -228,7 +759,7 @@ error_tm:
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);
PDEBUG(DDCF77, DEBUG_NOTICE, "The time transmitting: %s %s %d %02d:%02d:%02d %s %02d\n", week_day[wday], month_name[tm->tm_mon + 1], tm->tm_mday, tm->tm_hour, tm->tm_min, tm->tm_sec, time_zone[zone], tm->tm_year + 1900);
if ((tm->tm_isdst > 0) != (isdst_next_hour > 0))
frame |= (uint64_t)1 << 16;
@ -270,19 +801,40 @@ error_tm:
frame |= (uint64_t)(p & 1) << 58;
tx->data_frame = frame;
for (i = 0, j = 0; i < 59; i++) {
if (i == 1 || i == 15 || i == 21 || i == 29 || i == 36 || i == 42 || i == 45 || i == 50)
tx->data_string[j++] = ' ';
tx->data_string[j++] = '0' + ((frame >> i) & 1);
}
tx->data_string[j] = '\0';
PDEBUG(DDSP, DEBUG_INFO, "Start transmission of frame:\n");
PDEBUG(DDSP, DEBUG_INFO, "0 Wetterdaten Info 1 Minute P StundeP Tag WoT Monat Jahr P\n");
PDEBUG(DDSP, DEBUG_INFO, "%s\n", tx->data_string);
}
if (second == 59)
symbol = 'm';
else symbol = ((tx->data_frame >> second) & 1) + '0';
else
symbol = ((tx->data_frame >> second) & 1) + '0';
PDEBUG(DDSP, DEBUG_DEBUG, "Trasmitting symbol '%c' (Bit %d)\n", symbol, second);
return symbol;
}
static void rx_symbol(dcf77_t *dcf77, char symbol);
/* encode one minute */
void dcf77_encode(dcf77_t *dcf77, sample_t *samples, int length)
{
#ifdef DEBUG_LOOP
/* mute and speed up by factor 20 */
memset(samples, 0, sizeof(*samples) * length);
sample_t test_sample[length * 20];
samples = test_sample;
length *= 20;
#endif
dcf77_tx_t *tx = &dcf77->tx;
double carrier_phase, test_phase;
int i;
@ -310,6 +862,9 @@ void dcf77_encode(dcf77_t *dcf77, sample_t *samples, int length)
tx->timestamp += 60;
}
tx->symbol = tx_symbol(dcf77, tx->timestamp, tx->second);
#ifdef DEBUG_LOOP
rx_symbol(dcf77, tx->symbol);
#endif
}
switch (tx->symbol) {
case '0':
@ -345,7 +900,146 @@ void dcf77_encode(dcf77_t *dcf77, sample_t *samples, int length)
tx->test_phase = test_phase;
}
static void rx_frame(uint64_t frame)
/*
* RX part
*/
/* display weather data from weather frame */
static void display_weather(uint32_t weather, int minute, int utc_hour)
{
int dataset = ((utc_hour + 2) % 24) * 20 + (minute / 3); /* data sets since 22:00 UTC */
int value;
PDEBUG(DFRAME, DEBUG_INFO, "Decoding weather for dataset %d/480\n", dataset);
printf("Received Weather INFO\n");
printf("---------------------\n");
printf("Time (UTC): %02d:%02d\n", utc_hour, minute);
printf("Dataset: %s\n", datasets_0_59[dataset / 60]);
value = dataset % 60;
printf("Region: %d = %s\n", value, region[value]);
if ((dataset / 60) < 7) {
value = (weather >> 0) & 0xf;
printf("Weather (day): %s = %s\n", show_bits(value, 4), weathers_day[value]);
value = (weather >> 4) & 0xf;
printf("Weather (night): %s = %s\n", show_bits(value, 4), weathers_night[value]);
}
if (((dataset / 60) & 1) == 0) {
/* even datasets, this is 'Day' data */
if (((weather >> 15) & 1) == 0) {
value = (weather >> 8) & 0xf;
printf("Extreme weather: %s = %s\n", show_bits(value, 4), extremeweathers[value]);
} else {
value = (weather >> 8) & 0x3;
printf("Relative weather: %s = %s\n", show_bits(value, 2), anomaly1[value]);
value = (weather >> 10) & 0x3;
printf("Sunshine: %s = %s\n", show_bits(value, 2), anomaly1[value]);
}
value = (weather >> 12) & 0x7;
printf("Rain Probability: %s = %s\n", show_bits(value, 3), probabilities[value]);
value = (weather >> 15) & 0x1;
printf("Anomaly: %s = %s\n", show_bits(value, 1), yesno[value]);
display_weather_temperature("Temperature (day): ", weather);
} else {
/* odd datasets, this is 'Night' data */
if (((weather >> 15) & 1) == 0) {
value = (weather >> 8) & 0xf;
printf("Wind direction: %s = %s\n", show_bits(value, 4), winddirections[value]);
} else {
value = (weather >> 8) & 0x3;
printf("Relative weather: %s = %s\n", show_bits(value, 2), anomaly1[value]);
value = (weather >> 10) & 0x3;
printf("Sunshine: %s = %s\n", show_bits(value, 2), anomaly1[value]);
}
value = (weather >> 12) & 0x7;
printf("Wind strength: %s = %s\n", show_bits(value, 3), windstrengths[value]);
value = (weather >> 15) & 0x1;
printf("Anomaly: %s = %s\n", show_bits(value, 1), yesno[value]);
if ((dataset / 60) < 7)
display_weather_temperature("Temperature (night): ", weather);
}
if ((dataset / 60) == 7) {
printf("Dataset: %s\n", 60 + datasets_60_89[(dataset % 60) / 30]);
value = 60 + (dataset % 30);
printf("Region: %d = %s\n", value, region[value]);
value = (weather >> 0) & 0xf;
printf("Weather (day): %s = %s\n", show_bits(value, 4), weathers_day[value]);
value = (weather >> 4) & 0xf;
printf("Weather (night): %s = %s\n", show_bits(value, 4), weathers_night[value]);
display_weather_temperature("Temperature: ", weather);
}
}
/* reset weather frame */
static void rx_weather_reset(dcf77_rx_t *rx)
{
rx->weather_index = 0;
rx->weather_cipher = 0;
rx->weather_key = 0;
}
/* receive weather chunk */
static void rx_weather(dcf77_rx_t *rx, int minute, int hour, int zone, uint64_t frame)
{
int index = (minute + 2) % 3;
int utc_hour;
int32_t weather;
if (rx->weather_index == 0 && index != 0) {
PDEBUG(DFRAME, DEBUG_INFO, "Skipping weather info chunk, waiting for new start of weather info.\n");
return;
}
if (index == 0) {
rx_weather_reset(rx);
rx->weather_cipher |= (frame >> 2) & 0x3f; /* bit 2-7 */
rx->weather_cipher |= (frame >> 3) & 0x0fc0; /* bit 9-14 */
rx->weather_index++;
if (((frame & 0x0002)) || ((frame & 0x0100)) || !rx->weather_cipher) {
PDEBUG(DFRAME, DEBUG_INFO, "There is no weather info in this received minute.\n");
rx_weather_reset(rx);
return;
}
PDEBUG(DFRAME, DEBUG_INFO, "Got first chunk of weather info.\n");
return;
}
if (rx->weather_index == 1 && index == 1) {
PDEBUG(DFRAME, DEBUG_INFO, "Got second chunk of weather info.\n");
rx->weather_cipher |= (frame << 11) & 0x3fff000; /* bit 1-14 */
rx->weather_key |= (frame >> 21) & 0x7f;
rx->weather_key |= ((frame >> 29) & 0x3f) << 8;
rx->weather_key |= ((frame >> 36) & 0x3f) << 16;
rx->weather_key |= ((frame >> 45) & 0x1f) << 24;
rx->weather_key |= ((frame >> 42) & 0x07) << 29;
rx->weather_key |= ((frame >> 50) & 0xff) << 32;
rx->weather_index++;
return;
}
if (rx->weather_index == 2 && index == 2) {
PDEBUG(DFRAME, DEBUG_INFO, "Got third chunk of weather info.\n");
rx->weather_cipher |= (frame << 25) & 0xfffc000000; /* bit 1-14 */
weather = weather_decode(rx->weather_cipher, rx->weather_key);
if (weather < 0)
PDEBUG(DFRAME, DEBUG_NOTICE, "Failed to decrypt weather info, checksum error.\n");
else {
/* convert hour to UTC */
utc_hour = hour - 1;
if (zone & 1)
utc_hour--;
if (utc_hour < 0)
utc_hour += 24;
/* in index 2 we transmit minute + 3 (next minute), so we substract 3 */
display_weather(weather, (minute + 57) % 60, utc_hour);
}
rx_weather_reset(rx);
return;
}
rx_weather_reset(rx);
PDEBUG(DFRAME, DEBUG_INFO, "Got weather info chunk out of order, waiting for new start of weather info.\n");
}
/* decode time from received data */
static void rx_frame(dcf77_rx_t *rx, uint64_t frame)
{
int zone;
int minute_one, minute_ten, minute = -1;
@ -428,15 +1122,34 @@ static void rx_frame(uint64_t frame)
PDEBUG(DFRAME, DEBUG_INFO, "Year : %02d\n", year);
}
if (minute >= 0 && hour >= 0 && day >= 0 && wday >= 0 && month >= 0 && year >= 0)
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
rx_weather(rx, minute, hour, zone, frame);
} else {
PDEBUG(DDCF77, DEBUG_NOTICE, "The received time is invalid!\n");
rx_weather_reset(rx);
}
}
/* test routing for test data */
void rx_frame_test(dcf77_t *dcf77, const char *string)
{
uint64_t frame = 0;
int i;
puts(string);
for (i = 0; i < 59; i++) {
frame |= (uint64_t)(string[i] & 1) << i;
}
rx_frame(&dcf77->rx, frame);
}
/* receive one symbol = one second */
static void rx_symbol(dcf77_t *dcf77, char symbol)
{
dcf77_rx_t *rx = &dcf77->rx;
double second = -NAN;
PDEBUG(DDSP, DEBUG_DEBUG, "Received symbol '%c'\n", symbol);
@ -445,33 +1158,48 @@ static void rx_symbol(dcf77_t *dcf77, char symbol)
PDEBUG(DDSP, DEBUG_INFO, "Reception of frame has started\n");
rx->data_receive = 1;
rx->data_index = 0;
rx->string_index = 0;
second = 0;
}
} else {
if (symbol == 'm') {
if (rx->data_index == 59) {
rx->data_string[rx->data_index] = '\0';
rx->data_string[rx->string_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);
rx->string_index = 0;
PDEBUG(DDSP, DEBUG_INFO, "Received complete frame:\n");
PDEBUG(DDSP, DEBUG_INFO, "0 Wetterdaten Info 1 Minute P StundeP Tag WoT Monat Jahr P\n");
PDEBUG(DDSP, DEBUG_INFO, "%s\n", rx->data_string);
rx_frame(rx, rx->data_frame);
second = 0;
} else {
PDEBUG(DDSP, DEBUG_INFO, "Short read, frame too short\n");
rx->data_index = 0;
rx->string_index = 0;
rx_weather_reset(rx);
}
} else {
if (rx->data_index == 59) {
PDEBUG(DDSP, DEBUG_INFO, "Long read, frame too long\n");
rx->data_receive = 0;
rx_weather_reset(rx);
} else {
rx->data_string[rx->data_index++] = symbol;
if (rx->data_index == 1 || rx->data_index == 15 || rx->data_index == 21 || rx->data_index == 29 || rx->data_index == 36 || rx->data_index == 42 || rx->data_index == 45 || rx->data_index == 50)
rx->data_string[rx->string_index++] = ' ';
rx->data_string[rx->string_index++] = symbol;
rx->data_index++;
rx->data_frame >>= 1;
rx->data_frame |= (uint64_t)(symbol & 1) << 58;
second = rx->data_index;
}
}
}
display_measurements_update(dcf77->dmp_current_second, second, 0.0);
}
//#define DEBUG_SAMPLE
/* decode radio wave and extract each bit / second */
void dcf77_decode(dcf77_t *dcf77, sample_t *samples, int length)
{
dcf77_rx_t *rx = &dcf77->rx;
@ -481,6 +1209,9 @@ void dcf77_decode(dcf77_t *dcf77, sample_t *samples, int length)
display_wave(&dcf77->dispwav, samples, length, 1.0);
#ifdef DEBUG_LOOP
return;
#endif
if (!rx->enable)
return;

View File

@ -15,7 +15,18 @@ typedef struct dcf77_tx {
int second;
char symbol;
uint64_t data_frame;
char data_string[100]; /* 60 digits + spaces + '\0' */
int test_tone;
int weather;
int weather_day;
int weather_night;
int extreme;
int rain;
int wind_dir;
int wind_bft;
int temperature_day;
int temperature_night;
uint64_t weather_cipher;
} dcf77_tx_t;
typedef struct dcf77_rx {
@ -29,9 +40,12 @@ typedef struct dcf77_rx {
int clock_count;
double value_level, value_short, value_long; /* measured values */
int data_receive, data_index;
char data_string[60]; /* 59 digits + '\0' */
char data_string[100]; /* 60 digits + spaces + '\0' */
int string_index;
uint64_t data_frame;
iir_filter_t clock_lp[2]; /* filters received carrier signal */
int weather_index;
uint64_t weather_cipher;
uint64_t weather_key;
} dcf77_rx_t;
typedef struct dcf77 {
@ -43,6 +57,7 @@ typedef struct dcf77 {
dispmeasparam_t *dmp_input_level;
dispmeasparam_t *dmp_signal_level;
dispmeasparam_t *dmp_signal_quality;
dispmeasparam_t *dmp_current_second;
/* wave */
dispwav_t dispwav; /* display wave form */
@ -52,6 +67,10 @@ 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_tx_start(dcf77_t *dcf77, time_t timestamp, double sub_sec);
void dcf77_encode(dcf77_t *dcf77, sample_t *samples, int length);
void dcf77_decode(dcf77_t *dcf77, sample_t *samples, int length);
void list_weather(void);
time_t dcf77_start_weather(time_t timestamp, int region, int offset);
void dcf77_set_weather(dcf77_t *dcf77, int weather_day, int weather_night, int extreme, int rain, int wind_dir, int wind_bft, int temperature_day, int temperature_night);

16
src/dcf77/image.c Normal file
View File

@ -0,0 +1,16 @@
#include <stdio.h>
const char *aaimage[] = {
"",
"@W * DCF77 with weather info *",
"",
" @Y\\__/",
" @w_______@Y/ \\__",
" @w/ ____\\_@Y/@w_",
" \\___/@r10:08:33@w\\",
" @B/ @w\\________/",
" @B/ @W*@B/ @W*@B/ @W*@B/ @W*@B/",
" @W*@B/ @W*@B/ @W*@B/ @W*@B/",
"",
NULL
};

View File

@ -27,11 +27,14 @@
#include <termios.h>
#include <sched.h>
#include <time.h>
#include <math.h>
#include "../libdebug/debug.h"
#include "../liboptions/options.h"
#include "../libsample/sample.h"
#include "../libsound/sound.h"
#include "../libaaimage/aaimage.h"
#include "dcf77.h"
#include "cities.h"
int num_kanal = 1;
dcf77_t *dcf77 = NULL;
@ -40,7 +43,17 @@ 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 double timestamp = -1;
static int weather = 0;
static int weather_day;
static int weather_night;
static int extreme;
static int rain;
static int wind_dir;
static int wind_bft;
static int temperature_day;
static int temperature_night;
static int region = -1, region_advance;
static int double_amplitude = 0;
static int test_tone = 0;
static int dsp_interval = 1; /* ms */
@ -110,8 +123,6 @@ static time_t feierabend_time()
t = get_time();
tm = localtime(&t);
if (!tm)
return -1;
tm->tm_hour = 17;
tm->tm_min = 0;
@ -148,7 +159,7 @@ void print_help(void)
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(" 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");
@ -156,6 +167,29 @@ void print_help(void)
printf(" --geburtstag\n");
printf(" --birthday\n");
printf(" Use fake time stamp that equals birth of the author.\n");
printf(" -W --weather <weather> <extreme> <rain> <wind dir> <wind bft> <temperature>\n");
printf(" Send these weather info for all regions / all days.\n");
printf(" See -L for infos on values.\n");
printf(" weather = 1..15 for common day and night weather.\n");
printf(" weather = 1..15,1..15 for specific day and night weather.\n");
printf(" extreme = 0..15 for extreme weather conditions.\n");
printf(" rain = 0..100 for rain/show probability. (closest is used)\n");
printf(" wind dir = N | NE | E | SE | S | SW | W | NW | 0 for wind direction.\n");
printf(" wind bft = <bft> for wind speed in bft. (closest is used)\n");
printf(" temerature = <celsius> for common min and max temperature.\n");
printf(" temerature = <celsius>,<celsius> for specific min and max temperature.\n");
printf(" --beach-party\n");
printf(" Beach weather, equivalent to -W 1 0 0 0 2 35,20\n");
printf(" --santa-claus\n");
printf(" --muenster-2005\n");
printf(" Deep snow, equivalent to -W 7 1 100 E 3 1,-1\n");
printf(" -A --at-region <region> <advance minutes>\n");
printf(" Alter time, so that the weather of the given region is transmitted.\n");
printf(" To allow the receiver to sync, give time to advance in minutes.\n");
printf(" -L --list\n");
printf(" List all regions / weather values.\n");
printf(" -C --city <name fragment>\n");
printf(" Search for city (case insensitive) and display its region code.\n");
printf(" -D --double-amplitude\n");
printf(" Transmit with double amplitude by using differential stereo output.\n");
printf(" --test-tone\n");
@ -173,8 +207,11 @@ void print_help(void)
#define OPT_F2 1002
#define OPT_G1 1003
#define OPT_G2 1004
#define OPT_TEST_TONE 1005
#define OPT_FAST_MATH 1006
#define OPT_BEACH 1005
#define OPT_SANTA 1006
#define OPT_MUENSTER 1007
#define OPT_TEST_TONE 1008
#define OPT_FAST_MATH 1009
static void add_options(void)
{
@ -190,15 +227,25 @@ static void add_options(void)
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('W', "weather", 6);
option_add(OPT_BEACH, "beach-party", 0);
option_add(OPT_SANTA, "santa-claus", 0);
option_add(OPT_MUENSTER, "muenster-2005", 0);
option_add('A', "at-region", 2);
option_add('L', "list", 0);
option_add('C', "city", 1);
option_add('D', "double-amplitude", 0);
option_add(OPT_TEST_TONE, "test-tone", 0);
option_add('r', "realtime", 1);
option_add(OPT_FAST_MATH, "fast-math", 0);
}
static const char *wind_dirs[8] = { "N", "NE", "E", "SE", "S", "SW", "W", "NW" };
static int handle_options(int short_option, int argi, char **argv)
{
int rc;
char *string, *string1;
int rc, i;
switch (short_option) {
case 'h':
@ -233,7 +280,6 @@ static int handle_options(int short_option, int argi, char **argv)
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;
@ -247,6 +293,79 @@ static int handle_options(int short_option, int argi, char **argv)
case OPT_G2:
timestamp = 115099200 - 70;
break;
case 'W':
if (weather) {
no_multiple_weathers:
fprintf(stderr, "You cannot define more than one weather situation.\n");
return -EINVAL;
}
weather = 1;
string = options_strdup(argv[argi++]);
string1 = strsep(&string, ",");
weather_day = atoi(string1);
if (string)
weather_night = atoi(string);
else
weather_night = weather_day;
extreme = atoi(argv[argi++]);
rain = atoi(argv[argi++]);
/* if wind is not found, wind 8 (changable) is selected */
string = options_strdup(argv[argi++]);
for (i = 0; i < 8; i++) {
if (!strcasecmp(string, wind_dirs[i]))
break;
}
wind_dir = i;
wind_bft = atoi(argv[argi++]);
string = options_strdup(argv[argi++]);
string1 = strsep(&string, ",");
temperature_day = atoi(string1);
if (string)
temperature_night = atoi(string);
else
temperature_night = temperature_day;
break;
case OPT_BEACH:
if (weather)
goto no_multiple_weathers;
weather = 1;
weather_day = 1;
weather_night = 1;
extreme = 0;
rain = 0;
wind_dir = 8;
wind_bft = 2;
temperature_day = 35;
temperature_night = 20; /* tropical night >= 20 */
break;
case OPT_SANTA:
case OPT_MUENSTER:
if (weather)
goto no_multiple_weathers;
weather = 1;
weather_day = 7;
weather_night = 7;
extreme = 0;
rain = 100;
wind_dir = 6;
wind_bft = 3;
temperature_day = 1;
temperature_night = -1; /* freezing a little */
break;
case 'A':
region = atoi(argv[argi++]);
if (region < 0 || region > 89) {
fprintf(stderr, "Given region number is is invalid, please use -L for list of valid regions.\n");
return -EINVAL;
}
region_advance = atoi(argv[argi++]);
break;
case 'L':
list_weather();
return 0;
case 'C':
display_city(argv[argi++]);
return 0;
case OPT_TEST_TONE:
test_tone = 1;
break;
@ -407,8 +526,20 @@ int main(int argc, char *argv[])
fprintf(stderr, "Failed to create \"DCF77\" instance. Quitting!\n");
goto error;
}
if (weather)
dcf77_set_weather(dcf77, weather_day, weather_night, extreme, rain, wind_dir, wind_bft, temperature_day, temperature_night);
printf("\n");
/* no time stamp given, so we use our clock */
if (tx) {
if (timestamp < 0 && region < 0)
printf("No alternative time given, so you might not notice the difference between our transmission and the real DCF77 transmission.\n");
if (timestamp < 0)
timestamp = get_time();
if (region >= 0 && weather)
timestamp = dcf77_start_weather((time_t)timestamp, region, region_advance);
}
print_aaimage();
printf("DCF77 ready.\n");
/* prepare terminal */
@ -437,7 +568,7 @@ int main(int argc, char *argv[])
soundif_start();
if (tx)
dcf77_tx_start(dcf77, timestamp);
dcf77_tx_start(dcf77, (time_t)timestamp, fmod(timestamp, 1.0));
while (!quit) {
int w;

582
src/dcf77/weather.c Normal file
View File

@ -0,0 +1,582 @@
/* based on code found at:
https://github.com/FroggySoft/AlarmClock/blob/master/dcf77.cpp
https://github.com/tobozo/esp32-dcf77-weatherman/blob/master/dcf77.cpp
*/
#include <stdio.h>
#include <stdint.h>
#include <endian.h>
#include "../libdebug/debug.h"
#include "weather.h"
/// Container zum Konvertieren zwischen 4 Bytes und Uint.
union ByteUInt {
struct {
# if __BYTE_ORDER == __LITTLE_ENDIAN
uint8_t Byte0;
uint8_t Byte1;
uint8_t Byte2;
uint8_t Byte3;
# elif __BYTE_ORDER == __BIG_ENDIAN
uint8_t Byte3;
uint8_t Byte2;
uint8_t Byte1;
uint8_t Byte0;
# else
#error unsupported bitorder, please fix
# endif
} s;
uint32_t FullUint;
};
/// bit pattern for 0D,0E from 0B-0D
static const uint32_t mUintArrBitPattern12[12] = {
0x80000, //0b10000000000000000000 / 0D.3
0x00010, //0b00000000000000010000 / 0B.4
0x00008, //0b00000000000000001000 / 0B.3
0x00100, //0b00000000000100000000 / 0C.0
0x00080, //0b00000000000010000000 / 0B.7
0x01000, //0b00000001000000000000 / 0C.4
0x00800, //0b00000000100000000000 / 0C.3
0x10000, //0b00010000000000000000 / 0D.0
0x08000, //0b00001000000000000000 / 0C.7
0x00001, //0b00000000000000000001 / 0B.0
0x00000, //0b00000000000000000000 / xxxx
0x00000 //0b00000000000000000000 / xxxx
};
/// 12-15 from 16-19 (time)
static const uint32_t mUintArrBitPattern30_1[30] = {
0x00000200, //0b00000000000000000000001000000000 / 17.1
0x00000020, //0b00000000000000000000000000100000 / 16.5
0x02000000, //0b00000010000000000000000000000000 / 19.1
0x00000000, //0b00000000000000000000000000000000 / 1A.3
0x00000000, //0b00000000000000000000000000000000 / 1A.5
0x00000080, //0b00000000000000000000000010000000 / 16.7
0x40000000, //0b01000000000000000000000000000000 / 19.6
0x01000000, //0b00000001000000000000000000000000 / 19.0
0x04000000, //0b00000100000000000000000000000000 / 19.2
0x00000000, //0b00000000000000000000000000000000 / 1A.4
0x00010000, //0b00000000000000010000000000000000 / 18.0
0x00000000, //0b00000000000000000000000000000000 / 1A.2
0x00400000, //0b00000000010000000000000000000000 / 18.6
0x00000010, //0b00000000000000000000000000010000 / 16.4
0x00200000, //0b00000000001000000000000000000000 / 18.5
0x00080000, //0b00000000000010000000000000000000 / 18.3
0x00004000, //0b00000000000000000100000000000000 / 17.6
0x00000000, //0b00000000000000000000000000000000 / 1A.6
0x00020000, //0b00000000000000100000000000000000 / 18.1
0x00100000, //0b00000000000100000000000000000000 / 18.4
0x00008000, //0b00000000000000001000000000000000 / 17.7
0x00000040, //0b00000000000000000000000001000000 / 16.6
0x00001000, //0b00000000000000000001000000000000 / 17.4
0x00000400, //0b00000000000000000000010000000000 / 17.2
0x00000001, //0b00000000000000000000000000000001 / 16.0
0x80000000, //0b10000000000000000000000000000000 / 19.7
0x00000008, //0b00000000000000000000000000001000 / 16.3
0x00000002, //0b00000000000000000000000000000010 / 16.1
0x00040000, //0b00000000000001000000000000000000 / 18.2
0x10000000 //0b00010000000000000000000000000000 / 19.4
};
/// bit pattern for 12-15 from 1A (time2)
static const uint32_t mUintArrBitPattern30_2[30] = {
0x00, //0b00000000, /* 17.1
0x00, //0b00000000, /* 16.5
0x00, //0b00000000, /* 19.1
0x08, //0b00001000, /* 1A.3
0x20, //0b00100000, /* 1A.5
0x00, //0b00000000, /* 16.7
0x00, //0b00000000, /* 19.6
0x00, //0b00000000, /* 19.0
0x00, //0b00000000, /* 19.2
0x10, //0b00010000, /* 1A.4
0x00, //0b00000000, /* 18.0
0x04, //0b00000100, /* 1A.2
0x00, //0b00000000, /* 18.6
0x00, //0b00000000, /* 16.4
0x00, //0b00000000, /* 18.5
0x00, //0b00000000, /* 18.3
0x00, //0b00000000, /* 17.6
0x40, //0b01000000, /* 1A.6
0x00, //0b00000000, /* 18.1
0x00, //0b00000000, /* 18.4
0x00, //0b00000000, /* 17.7
0x00, //0b00000000, /* 16.6
0x00, //0b00000000, /* 17.4
0x00, //0b00000000, /* 17.2
0x00, //0b00000000, /* 16.0
0x00, //0b00000000, /* 19.7
0x00, //0b00000000, /* 16.3
0x00, //0b00000000, /* 16.1
0x00, //0b00000000, /* 18.2
0x00 //0b00000000, /* 19.4
};
/// 12-14 from 1C-1E (result from F)
static const uint32_t mUintArrBitPattern20[20] = {
0x000004, //0b000000000000000000000100 / 1C.2
0x002000, //0b000000000010000000000000 / 1E.5
0x008000, //0b000000001000000000000000 / 1E.7
0x400000, //0b010000000000000000000000 / 1D.6
0x000100, //0b000000000000000100000000 / 1E.0
0x100000, //0b000100000000000000000000 / 1D.4
0x000400, //0b000000000000010000000000 / 1E.2
0x800000, //0b100000000000000000000000 / 1D.7
0x040000, //0b000001000000000000000000 / 1D.2
0x020000, //0b000000100000000000000000 / 1D.1
0x000008, //0b000000000000000000001000 / 1C.3
0x000200, //0b000000000000001000000000 / 1E.1
0x004000, //0b000000000100000000000000 / 1E.6
0x000002, //0b000000000000000000000010 / 1C.1
0x001000, //0b000000000001000000000000 / 1E.4
0x080000, //0b000010000000000000000000 / 1D.3
0x000800, //0b000000000000100000000000 / 1E.3
0x200000, //0b001000000000000000000000 / 1D.5
0x010000, //0b000000010000000000000000 / 1D.0
0x000001 //0b000000000000000000000001 / 1C.0
};
/// bit pattern for 12-15 from 16-19 (1/3)
static const uint64_t mByteArrLookupTable1C_1[8] = {
0xBB0E22C573DFF76D, 0x90E9A1381C844A56,
0x648D280BD1BA9352, 0x1CC5A7F0E97F364E,
0xC1773DB3AAE00C6F, 0x1488F62BD2995E45,
0x1F7096D3B30BFCEE, 0x8142CA34A5582967
};
/// bit pattern for 12-15 from 16-19 (2/3)
static const uint64_t mByteArrLookupTable1C_2[8] = {
0xAB3DFC7465E60E4F, 0x9711D85983C2BA20,
0xC51BD2584937017D, 0x93FAE02F66B4AC8E,
0xB7CC43FF5866EB35, 0x822A99DD007114AE,
0x4EB1F7701852AA9F, 0xD56BCC3D0483E926
};
/// bit pattern for 12-15 from 16-19 (3/3)
static const uint64_t mByteArrLookupTable1C_3[8] = {
0x0A02000F06070D08, 0x030C0B050901040E,
0x0209050D0C0E0F08, 0x06070B01000A0403,
0x08000D0F010C0306, 0x0B0409050A07020E,
0x030D000C09060F0B, 0x010E080A02070405
};
/// Container, which contains all former global vars
typedef struct DataContainer {
/// Registers R12 to R15
union ByteUInt mByteUint1;
/// Registers R08 to R0A
union ByteUInt mByteUint2;
/// Registers R0B to R0E
union ByteUInt mByteUint3;
/// Registers R1C to R1E
union ByteUInt mByteUint4;
uint8_t mByteUpperTime2;//, mByteR1B;
uint32_t mUintLowerTime;
} DataContainer_t;
static int32_t GetWeatherFromPlain(uint8_t *PlainBytes)
{
uint32_t result;
uint32_t checkSum;
checkSum = PlainBytes[2] & 0x0f;
checkSum <<= 8;
checkSum |= PlainBytes[1];
checkSum <<= 4;
checkSum |= PlainBytes[0] >> 4;
if (checkSum != 0x2501)
return -1;
result = PlainBytes[0] & 0x0f;
result <<= 8;
result |= PlainBytes[4];
result <<= 8;
result |= PlainBytes[3];
result <<= 4;
result |= PlainBytes[2] >> 4;
return result;
}
static uint8_t *GetPlainFromWeather(uint32_t weather)
{
static uint8_t result[5];
weather <<= 4;
result[1] = 0x50;
result[2] = (weather & 0xf0) | 0x02;
weather >>= 8;
result[3] = weather & 0xff;
weather >>= 8;
result[4] = weather & 0xff;
weather >>= 8;
result[0] = (weather & 0x0f) | 0x10;
return result;
}
static void CopyTimeToByteUint(uint8_t *data, uint8_t *key, DataContainer_t *container)
{
int i;
for (i = 0; i < 4; i++)
{
container->mUintLowerTime <<= 8;
container->mUintLowerTime |= key[3 - i];
}
container->mByteUpperTime2 = key[4];
// copy R
container->mByteUint3.s.Byte0 = data[2];
container->mByteUint3.s.Byte1 = data[3];
container->mByteUint3.s.Byte2 = data[4];
container->mByteUint3.FullUint >>= 4;
// copy L
container->mByteUint2.s.Byte0 = data[0];
container->mByteUint2.s.Byte1 = data[1];
container->mByteUint2.s.Byte2 = (uint8_t)(data[2] & 0x0F);
}
static void ShiftTimeRight(int round, DataContainer_t *container)
{
int count;
uint8_t tmp;
if ((round == 16) || (round == 8) || (round == 7) || (round == 3))
count = 2;
else
count = 1;
while (count-- != 0)
{
tmp = 0;
if ((container->mUintLowerTime & 0x00100000) != 0) // save time bit 20
tmp = 1;
container->mUintLowerTime &= 0xFFEFFFFF;
if ((container->mUintLowerTime & 1) != 0)
container->mUintLowerTime |= 0x00100000; // copy time bit 0 to time bit 19
container->mUintLowerTime >>= 1; // time >>= 1
if ((container->mByteUpperTime2 & 1) != 0)
container->mUintLowerTime |= 0x80000000;
container->mByteUpperTime2 >>= 1;
if (tmp != 0)
container->mByteUpperTime2 |= 0x80; // insert time bit 20 to time bit 39
}
}
static void ShiftTimeLeft(int round, DataContainer_t *container)
{
int count;
uint8_t tmp;
if ((round == 16) || (round == 8) || (round == 7) || (round == 3))
count = 2;
else
count = 1;
while (count-- != 0)
{
tmp = 0;
if ((container->mByteUpperTime2 & 0x80) != 0)
tmp = 1;
container->mByteUpperTime2 <<= 1;
if ((container->mUintLowerTime & 0x80000000) != 0)
container->mByteUpperTime2 |= 1;
container->mUintLowerTime <<= 1;
if ((container->mUintLowerTime & 0x00100000) != 0)
container->mUintLowerTime |= 1;
container->mUintLowerTime &= 0xFFEFFFFF;
if (tmp != 0)
container->mUintLowerTime |= 0x00100000;
}
}
static void ExpandR(DataContainer_t *container)
{
uint32_t tmp;
int i;
container->mByteUint3.FullUint &= 0x000FFFFF; // clear 0D(4-7),0E
tmp = 0x00100000; // and set bits form 0B-0D(0-3)
for (i = 0; i < 12; i++)
{
if ((container->mByteUint3.FullUint & mUintArrBitPattern12[i]) != 0)
container->mByteUint3.FullUint |= tmp;
tmp <<= 1;
}
}
static void ExpandL(DataContainer_t *container)
{
uint32_t tmp;
int i;
container->mByteUint2.FullUint &= 0x000FFFFF; // clear 0D(4-7),0E
tmp = 0x00100000; // and set bits form 0B-0D(0-3)
for (i = 0; i < 12; i++)
{
if ((container->mByteUint2.FullUint & mUintArrBitPattern12[i]) != 0)
container->mByteUint2.FullUint |= tmp;
tmp <<= 1;
}
}
static void CompressKey(DataContainer_t *container)
{
uint32_t tmp;
int i;
container->mByteUint1.FullUint = 0; // clear 12-15
tmp = 0x00000001; // and set bits from 16-1A (time)
for (i = 0; i < 30; i++)
{
if ((container->mUintLowerTime & mUintArrBitPattern30_1[i]) != 0 || (container->mByteUpperTime2 & mUintArrBitPattern30_2[i]) != 0)
container->mByteUint1.FullUint |= tmp;
tmp <<= 1;
}
}
static void DoSbox(DataContainer_t *container)
{
uint8_t tmp, helper; //mByteR1B;
int i;
helper = container->mByteUint1.s.Byte3; // R1B = R15;
container->mByteUint1.s.Byte3 = container->mByteUint1.s.Byte2; // R15 = R14
// INNER LOOP
for (i = 5; i > 0; i--)
{
if ((i & 1) == 0) // round 4,2
{
tmp = (uint8_t)(container->mByteUint1.s.Byte0 >> 4); // swap R12
tmp |= (uint8_t)((container->mByteUint1.s.Byte0 & 0x0f) << 4);
container->mByteUint1.s.Byte0 = tmp;
}
container->mByteUint1.s.Byte3 &= 0xF0; // set R1C
tmp = (uint8_t)((container->mByteUint1.s.Byte0 & 0x0F) | container->mByteUint1.s.Byte3);
if ((i & 4) != 0)
tmp = mByteArrLookupTable1C_1[(tmp & 0x38) >> 3] >> (56 - (tmp & 0x07) * 8);
if ((i & 2) != 0)
tmp = mByteArrLookupTable1C_2[(tmp & 0x38) >> 3] >> (56 - (tmp & 0x07) * 8);
else if (i == 1)
tmp = mByteArrLookupTable1C_3[(tmp & 0x38) >> 3] >> (56 - (tmp & 0x07) * 8);
if ((i & 1) != 0)
container->mByteUint4.s.Byte0 = (uint8_t)(tmp & 0x0F);
else
container->mByteUint4.s.Byte0 |= (uint8_t)(tmp & 0xF0);
if ((i & 1) == 0) // copy 14->13->12, 1C->1E->1D
{
tmp = container->mByteUint1.s.Byte3;
container->mByteUint1.FullUint >>= 8;
container->mByteUint1.s.Byte3 = tmp;
container->mByteUint4.FullUint <<= 8;
}
container->mByteUint1.s.Byte3 >>= 1; // rotate R1B>R15 twice
if ((helper & 1) != 0)
container->mByteUint1.s.Byte3 |= 0x80;
helper >>= 1;
container->mByteUint1.s.Byte3 >>= 1;
if ((helper & 1) != 0)
container->mByteUint1.s.Byte3 |= 0x80;
helper >>= 1;
} // end of inner loop
}
static void DoPbox(DataContainer_t *container)
{
uint32_t tmp;
int i;
container->mByteUint1.FullUint = 0xFF000000; // clear 12-14
tmp = 0x00000001; // and set bits from 1C-1E (result from F)
for (i = 0; i < 20; i++)
{
if ((container->mByteUint4.FullUint & mUintArrBitPattern20[i]) != 0)
container->mByteUint1.FullUint |= tmp;
tmp <<= 1;
}
}
/* modified DES decrypt using strings */
static uint8_t *Decrypt(uint8_t *cipher, uint8_t *key)
{
DataContainer_t container;
int i;
static uint8_t plain[5];
CopyTimeToByteUint(cipher, key, &container);
// OUTER LOOP 1
for (i = 16; i > 0; i--)
{
ShiftTimeRight(i, &container);
ExpandR(&container);
CompressKey(&container);
// expR XOR compr.Key
container.mByteUint1.FullUint ^= container.mByteUint3.FullUint; // 12-15 XOR 0B-0E
container.mByteUint3.s.Byte2 &= 0x0F; // clear 0D(4-7)
DoSbox(&container);
DoPbox(&container);
// L XOR P-Boxed Round-Key (L')
container.mByteUint1.FullUint ^= container.mByteUint2.FullUint;
// L = R
container.mByteUint2.FullUint = container.mByteUint3.FullUint & 0x00FFFFFF;
// R = L'
container.mByteUint3.FullUint = container.mByteUint1.FullUint & 0x00FFFFFF;
} // end of outer loop 1
container.mByteUint3.FullUint <<= 4;
container.mByteUint2.s.Byte2 &= 0x0F;
container.mByteUint2.s.Byte2 |= (uint8_t)(container.mByteUint3.s.Byte0 & 0xF0);
plain[0] = container.mByteUint2.s.Byte0;
plain[1] = container.mByteUint2.s.Byte1;
plain[2] = container.mByteUint2.s.Byte2;
plain[3] = container.mByteUint3.s.Byte1;
plain[4] = container.mByteUint3.s.Byte2;
return plain;
}
/* modified DES encrypt using strings */
static uint8_t *Encrypt(uint8_t *plain, uint8_t *key)
{
static uint8_t cipher[5];
DataContainer_t container;
int i;
CopyTimeToByteUint(plain, key, &container);
// OUTER LOOP 1
for (i = 1; i < 17; i++)
{
ExpandL(&container);
CompressKey(&container);
// expR XOR compr.Key
container.mByteUint1.FullUint ^= container.mByteUint2.FullUint; // L' XOR compr.Key
container.mByteUint3.s.Byte2 &= 0x0F; // clear 0D(4-7)
DoSbox(&container);
DoPbox(&container);
// L XOR P-Boxed Round-Key (L')
container.mByteUint1.FullUint ^= container.mByteUint3.FullUint;
// L = R
container.mByteUint3.FullUint = container.mByteUint2.FullUint & 0x00FFFFFF;
// R = L'
container.mByteUint2.FullUint = container.mByteUint1.FullUint & 0x00FFFFFF;
ShiftTimeLeft(i, &container);
} // end of outer loop 1
container.mByteUint3.FullUint <<= 4;
container.mByteUint2.s.Byte2 &= 0x0F;
container.mByteUint2.s.Byte2 |= (uint8_t)(container.mByteUint3.s.Byte0 & 0xF0);
cipher[0] = container.mByteUint2.s.Byte0;
cipher[1] = container.mByteUint2.s.Byte1;
cipher[2] = container.mByteUint2.s.Byte2;
cipher[3] = container.mByteUint3.s.Byte1;
cipher[4] = container.mByteUint3.s.Byte2;
return cipher;
}
//#define DEBUG_CIPER
/* decode given crypted frame and key
* return the weather info or -1 on checksum error
*/
int32_t weather_decode(uint64_t cipher, uint64_t key)
{
uint8_t CipherBytes[5];
uint8_t KeyBytes[5];
uint8_t *PlainBytes;
int32_t weather;
int i;
for (i = 0; i < 5; i++)
CipherBytes[i] = cipher >> (i * 8);
for (i = 0; i < 5; i++)
KeyBytes[i] = key >> (i * 8);
PlainBytes = Decrypt(CipherBytes, KeyBytes);
weather = GetWeatherFromPlain(PlainBytes);
#ifdef DEBUG_CIPER
printf("cipher=%s\n", debug_hex(CipherBytes, 5));
printf("key =%s\n", debug_hex(KeyBytes, 5));
printf("plain =%s\n", debug_hex(PlainBytes, 5));
if (weather < 0)
printf("weather=error\n");
else
printf("weather=%06x\n", weather);
weather_encode(weather, key);
#endif
return weather;
}
/* encode given weather info and key
* return crypted frame
*/
uint64_t weather_encode(uint32_t weather, uint64_t key)
{
uint8_t KeyBytes[5];
uint8_t *PlainBytes;
uint8_t *CipherBytes;
uint64_t cipher = 0;
int i;
PlainBytes = GetPlainFromWeather(weather);
for (i = 0; i < 5; i++)
KeyBytes[i] = key >> (i * 8);
CipherBytes = Encrypt(PlainBytes, KeyBytes);
#ifdef DEBUG_CIPER
printf("plain =%s\n", debug_hex(PlainBytes, 5));
printf("key =%s\n", debug_hex(KeyBytes, 5));
printf("cipher=%s\n", debug_hex(CipherBytes, 5));
#endif
for (i = 0; i < 5; i++)
cipher |= (uint64_t)(CipherBytes[i]) << (i * 8);
return cipher;
}

3
src/dcf77/weather.h Normal file
View File

@ -0,0 +1,3 @@
int32_t weather_decode(uint64_t cipher, uint64_t key);
uint64_t weather_encode(uint32_t weather, uint64_t key);

View File

@ -506,7 +506,7 @@ static void call_play_announcement(euro_call_t *call)
else
chunk[i] = 0.0;
}
int16_to_samples(spl, chunk, 160);
int16_to_samples_speech(spl, chunk, 160);
call_up_audio(call->callref, spl, 160);
}
@ -760,7 +760,7 @@ void call_down_release(int callref, int cause)
}
/* Receive audio from call instance. */
void call_down_audio(int __attribute__((unused)) callref, sample_t __attribute__((unused)) *samples, int __attribute__((unused)) count)
void call_down_audio(int __attribute__((unused)) callref, uint16_t __attribute__((unused)) sequence, uint32_t __attribute__((unused)) timestamp, uint32_t __attribute__((unused)) ssrc, sample_t __attribute__((unused)) *samples, int __attribute__((unused)) count)
{
}

View File

@ -595,15 +595,17 @@ void sender_send(sender_t *sender, sample_t *samples, uint8_t *power, int length
fuenf_t *fuenf = (fuenf_t *) sender;
sample_t *orig_samples = samples;
int orig_length = length;
int count;
int count, input_num;
sample_t *spl;
int pos;
int i;
/* speak through */
if (fuenf->state == FUENF_STATE_DURCHSAGE && fuenf->callref) {
jitter_load(&fuenf->sender.dejitter, samples, length);
memset(power, 1, length);
input_num = samplerate_upsample_input_num(&sender->srstate, length);
jitter_load(&sender->dejitter, samples, input_num);
samplerate_upsample(&sender->srstate, samples, input_num, samples, length);
} else {
/* send if something has to be sent. else turn transmitter off */
while ((count = encode(fuenf, samples, length))) {

View File

@ -390,7 +390,7 @@ void call_down_release(int callref, int cause)
}
/* Receive audio from call instance. */
void call_down_audio(int callref, sample_t *samples, int count)
void call_down_audio(int callref, uint16_t sequence, uint32_t timestamp, uint32_t ssrc, sample_t *samples, int count)
{
sender_t *sender;
fuenf_t *fuenf;
@ -403,11 +403,8 @@ void call_down_audio(int callref, sample_t *samples, int count)
if (!sender)
return;
if (fuenf->state == FUENF_STATE_DURCHSAGE) {
sample_t up[(int)((double)count * fuenf->sender.srstate.factor + 0.5) + 10];
count = samplerate_upsample(&fuenf->sender.srstate, samples, count, up);
jitter_save(&fuenf->sender.dejitter, up, count);
}
if (fuenf->state == FUENF_STATE_DURCHSAGE)
jitter_save(&fuenf->sender.dejitter, samples, count, 1, sequence, timestamp, ssrc);
}
void dump_info(void) {}

View File

@ -1240,13 +1240,17 @@ void fuvst_destroy(sender_t *sender)
void sender_send(sender_t *sender, sample_t *samples, uint8_t *power, int length)
{
fuvst_t *fuvst = (fuvst_t *) sender;
int input_num;
memset(power, 1, length);
if (fuvst->chan_type == CHAN_TYPE_ZZK)
v27_modem_send(&fuvst->modem, samples, length);
else
jitter_load(&fuvst->sender.dejitter, samples, length);
else {
input_num = samplerate_upsample_input_num(&sender->srstate, length);
jitter_load(&sender->dejitter, samples, input_num);
samplerate_upsample(&sender->srstate, samples, input_num, samples, length);
}
}
void sender_receive(sender_t *sender, sample_t *samples, int length, double __attribute__((unused)) rf_level_db)
@ -1280,7 +1284,7 @@ void sender_receive(sender_t *sender, sample_t *samples, int length, double __at
}
/* Receive audio from call instance. */
void call_down_audio(int callref, sample_t *samples, int count)
void call_down_audio(int callref, uint16_t sequence, uint32_t timestamp, uint32_t ssrc, sample_t *samples, int count)
{
sender_t *sender;
fuvst_t *fuvst;
@ -1293,11 +1297,8 @@ void call_down_audio(int callref, sample_t *samples, int count)
if (!sender)
return;
if (fuvst->callref) {
sample_t up[(int)((double)count * fuvst->sender.srstate.factor + 0.5) + 10];
count = samplerate_upsample(&fuvst->sender.srstate, samples, count, up);
jitter_save(&fuvst->sender.dejitter, up, count);
}
if (fuvst->callref)
jitter_save(&fuvst->sender.dejitter, samples, count, 1, sequence, timestamp, ssrc);
}
void call_down_clock(void) {}

View File

@ -255,7 +255,7 @@ void sender_receive(sender_t *sender, sample_t *samples, int length, double __at
v27_modem_receive(&sniffer->modem, samples, length);
}
void call_down_audio(int __attribute__((unused)) callref, sample_t __attribute__((unused)) *samples, int __attribute__((unused)) count) { }
void call_down_audio(int __attribute__((unused)) callref, uint16_t __attribute__((unused)) sequence, uint32_t __attribute__((unused)) timestamp, uint32_t __attribute__((unused)) ssrc, sample_t __attribute__((unused)) *samples, int __attribute__((unused)) count) { }
void call_down_clock(void) {}

View File

@ -1,24 +1,21 @@
AM_CPPFLAGS = -Wall -Wextra -g $(all_includes)
bin_PROGRAMS = \
jtacs
golay
jtacs_SOURCES = \
tones.c \
stations.c \
golay_SOURCES = \
golay.c \
dsp.c \
image.c \
main.c
jtacs_LDADD = \
golay_LDADD = \
$(COMMON_LA) \
../amps/libamps.a \
../amps/libusatone.a \
$(top_builddir)/src/liboptions/liboptions.a \
$(top_builddir)/src/libdebug/libdebug.a \
$(top_builddir)/src/libmobile/libmobile.a \
$(top_builddir)/src/libosmocc/libosmocc.a \
$(top_builddir)/src/libdisplay/libdisplay.a \
$(top_builddir)/src/libcompandor/libcompandor.a \
$(top_builddir)/src/libgoertzel/libgoertzel.a \
$(top_builddir)/src/libjitter/libjitter.a \
$(top_builddir)/src/libtimer/libtimer.a \
$(top_builddir)/src/libsamplerate/libsamplerate.a \
@ -32,13 +29,14 @@ jtacs_LDADD = \
-lm
if HAVE_ALSA
jtacs_LDADD += \
golay_LDADD += \
$(top_builddir)/src/libsound/libsound.a \
$(ALSA_LIBS)
endif
if HAVE_SDR
jtacs_LDADD += \
golay_LDADD += \
$(top_builddir)/src/libsdr/libsdr.a \
$(top_builddir)/src/libam/libam.a \
$(top_builddir)/src/libfft/libfft.a \
@ -46,3 +44,7 @@ jtacs_LDADD += \
$(SOAPY_LIBS)
endif
if HAVE_ALSA
AM_CPPFLAGS += -DHAVE_ALSA
endif

209
src/golay/dsp.c Normal file
View File

@ -0,0 +1,209 @@
/* GSC signal processing
*
* (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/>.
*/
#define CHAN gsc->sender.kanal
#include <stdio.h>
#include <stdint.h>
#include <stdlib.h>
#include <string.h>
#include <errno.h>
#include <math.h>
#include "../libsample/sample.h"
#include "../libdebug/debug.h"
#include "golay.h"
#include "dsp.h"
#define MAX_DISPLAY 1.4 /* something above speech level, no emphasis */
static void dsp_init_ramp(gsc_t *gsc)
{
double c;
int i;
PDEBUG_CHAN(DDSP, DEBUG_DEBUG, "Generating cosine shaped ramp table.\n");
for (i = 0; i < 256; i++) {
/* This is mathematically incorrect... */
if (i < 64)
c = 1.0;
else if (i >= 192)
c = -1.0;
else
c = cos((double)(i - 64) / 128.0 * M_PI);
gsc->fsk_ramp_down[i] = c * gsc->fsk_deviation * gsc->fsk_polarity;
gsc->fsk_ramp_up[i] = -gsc->fsk_ramp_down[i];
}
}
/* Init transceiver instance. */
int dsp_init_sender(gsc_t *gsc, int samplerate, double deviation, double polarity)
{
int rc;
PDEBUG_CHAN(DDSP, DEBUG_DEBUG, "Init DSP for transceiver.\n");
/* set modulation parameters */
// NOTE: baudrate equals modulation, because we have a raised cosine ramp of beta = 0.5
sender_set_fm(&gsc->sender, deviation, 600.0, deviation, MAX_DISPLAY);
gsc->fsk_bitduration = (double)samplerate / 600.0;
gsc->fsk_bitstep = 1.0 / gsc->fsk_bitduration;
PDEBUG_CHAN(DDSP, DEBUG_DEBUG, "Use %.4f samples for one bit duration @ %d.\n", gsc->fsk_bitduration, gsc->sender.samplerate);
gsc->fsk_tx_buffer_size = gsc->fsk_bitduration + 10; /* 1 bit, add some extra to prevent short buffer due to rounding */
gsc->fsk_tx_buffer = calloc(sizeof(sample_t), gsc->fsk_tx_buffer_size);
if (!gsc->fsk_tx_buffer) {
PDEBUG_CHAN(DDSP, DEBUG_ERROR, "No memory!\n");
rc = -ENOMEM;
goto error;
}
/* create deviation and ramp */
gsc->fsk_deviation = 1.0; // equals what we st at sender_set_fm()
gsc->fsk_polarity = polarity;
dsp_init_ramp(gsc);
return 0;
error:
dsp_cleanup_sender(gsc);
return -rc;
}
/* Cleanup transceiver instance. */
void dsp_cleanup_sender(gsc_t *gsc)
{
PDEBUG_CHAN(DDSP, DEBUG_DEBUG, "Cleanup DSP for transceiver.\n");
if (gsc->fsk_tx_buffer) {
free(gsc->fsk_tx_buffer);
gsc->fsk_tx_buffer = NULL;
}
}
/* encode one bit into samples
* input: bit
* output: samples
* return number of samples */
static int fsk_bit_encode(gsc_t *gsc, uint8_t bit)
{
/* alloc samples, add 1 in case there is a rest */
sample_t *spl;
double phase, bitstep, devpol;
int count;
uint8_t lastbit;
devpol = gsc->fsk_deviation * gsc->fsk_polarity;
spl = gsc->fsk_tx_buffer;
phase = gsc->fsk_tx_phase;
lastbit = gsc->fsk_tx_lastbit;
bitstep = gsc->fsk_bitstep * 256.0;
if (lastbit) {
if (bit) {
/* stay up */
do {
*spl++ = devpol;
phase += bitstep;
} while (phase < 256.0);
phase -= 256.0;
} else {
/* ramp down */
do {
*spl++ = gsc->fsk_ramp_down[(uint8_t)phase];
phase += bitstep;
} while (phase < 256.0);
phase -= 256.0;
lastbit = 0;
}
} else {
if (bit) {
/* ramp up */
do {
*spl++ = gsc->fsk_ramp_up[(uint8_t)phase];
phase += bitstep;
} while (phase < 256.0);
phase -= 256.0;
lastbit = 1;
} else {
/* stay down */
do {
*spl++ = -devpol;
phase += bitstep;
} while (phase < 256.0);
phase -= 256.0;
}
}
/* depending on the number of samples, return the number */
count = ((uintptr_t)spl - (uintptr_t)gsc->fsk_tx_buffer) / sizeof(*spl);
gsc->fsk_tx_phase = phase;
gsc->fsk_tx_lastbit = lastbit;
return count;
}
/* Process received audio stream from radio unit. */
void sender_receive(sender_t __attribute__((unused)) *sender, sample_t __attribute__((unused)) *samples, int __attribute__((unused)) length, double __attribute__((unused)) rf_level_db)
{
}
/* Provide stream of audio toward radio unit */
void sender_send(sender_t *sender, sample_t *samples, uint8_t *power, int length)
{
gsc_t *gsc = (gsc_t *) sender;
again:
/* get word */
if (!gsc->fsk_tx_buffer_length) {
int8_t bit = get_bit(gsc);
/* no message, power is off */
if (bit < 0) {
memset(samples, 0, sizeof(samples) * length);
memset(power, 0, length);
return;
}
/* encode */
gsc->fsk_tx_buffer_length = fsk_bit_encode(gsc, bit);
gsc->fsk_tx_buffer_pos = 0;
}
/* send encoded bit until end of source or destination buffer is reached */
while (length) {
*power++ = 1;
*samples++ = gsc->fsk_tx_buffer[gsc->fsk_tx_buffer_pos++];
length--;
if (gsc->fsk_tx_buffer_pos == gsc->fsk_tx_buffer_length) {
gsc->fsk_tx_buffer_length = 0;
break;
}
}
/* do again, if destination buffer is not yet full */
if (length)
goto again;
}

4
src/golay/dsp.h Normal file
View File

@ -0,0 +1,4 @@
int dsp_init_sender(gsc_t *gsc, int samplerate, double deviation, double polarity);
void dsp_cleanup_sender(gsc_t *gsc);

823
src/golay/golay.c Normal file
View File

@ -0,0 +1,823 @@
/* Golay/GSC transcoding (encoding only - maybe)
*
* (C) 2022 by Andreas Eversberg <jolly@eversberg.eu>
* All Rights Reserved
*
* Inspired by GSC code written by Brandon Creighton <cstone@pobox.com>.
*
* Inspired by GOLAY code written by Robert Morelos-Zaragoza
* <robert@spectra.eng.hawaii.edu>.
*
* 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/>.
*/
/* Golay code was is use since 1973, the GSC extension was used after 1982.
*/
#include <stdio.h>
#include <stdint.h>
#include <stdlib.h>
#include <unistd.h>
#include <string.h>
#include <errno.h>
#include <sys/types.h>
#include "../libsample/sample.h"
#include "../libdebug/debug.h"
#include "../libmobile/call.h"
#include "../libmobile/main_mobile.h"
#include "../libmobile/cause.h"
#include "golay.h"
#include "dsp.h"
/* Create transceiver instance and link to a list. */
int golay_create(const char *kanal, double frequency, const char *device, int use_sdr, int samplerate, double rx_gain, double tx_gain, double deviation, double polarity, const char *message, const char *write_rx_wave, const char *write_tx_wave, const char *read_rx_wave, const char *read_tx_wave, int loopback)
{
gsc_t *gsc;
int rc;
gsc = calloc(1, sizeof(*gsc));
if (!gsc) {
PDEBUG(DGOLAY, DEBUG_ERROR, "No memory!\n");
return -ENOMEM;
}
PDEBUG(DGOLAY, DEBUG_DEBUG, "Creating 'GOLAY' instance for frequency = %s (sample rate %d).\n", kanal, samplerate);
/* init general part of transceiver */
rc = sender_create(&gsc->sender, kanal, frequency, frequency, 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(DGOLAY, DEBUG_ERROR, "Failed to init transceiver process!\n");
goto error;
}
/* init audio processing */
rc = dsp_init_sender(gsc, samplerate, deviation, polarity);
if (rc < 0) {
PDEBUG(DGOLAY, DEBUG_ERROR, "Failed to init audio processing!\n");
goto error;
}
gsc->tx = 1;
gsc->default_message = message;
PDEBUG(DGOLAY, DEBUG_NOTICE, "Created transmitter for frequency %s\n", kanal);
return 0;
error:
golay_destroy(&gsc->sender);
return rc;
}
static void golay_msg_destroy(gsc_t *gsc, gsc_msg_t *msg);
/* Destroy transceiver instance and unlink from list. */
void golay_destroy(sender_t *sender)
{
gsc_t *gsc = (gsc_t *) sender;
PDEBUG(DGOLAY, DEBUG_DEBUG, "Destroying 'GOLAY' instance for frequency = %s.\n", sender->kanal);
while (gsc->msg_list)
golay_msg_destroy(gsc, gsc->msg_list);
dsp_cleanup_sender(gsc);
sender_destroy(&gsc->sender);
free(gsc);
}
/* Create message and add to queue */
static gsc_msg_t *golay_msg_create(gsc_t *gsc, const char *address, const char *text, int force_type)
{
gsc_msg_t *msg, **msgp;
PDEBUG(DGOLAY, DEBUG_INFO, "Creating msg instance to page address '%s'.\n", address);
/* create */
msg = calloc(1, sizeof(*msg));
if (!msg) {
PDEBUG(DGOLAY, DEBUG_ERROR, "No mem!\n");
abort();
}
if (strlen(address) != sizeof(msg->address) - 1) {
PDEBUG(DGOLAY, DEBUG_NOTICE, "Address has incorrect length, cannot page!\n");
return NULL;
}
if (strlen(text) > sizeof(msg->data) - 1) {
PDEBUG(DGOLAY, DEBUG_NOTICE, "Given test is too long, cannot page!\n");
return NULL;
}
/* init */
strcpy(msg->address, address);
msg->force_type = force_type;
strcpy(msg->data, text);
/* link */
msgp = &gsc->msg_list;
while ((*msgp))
msgp = &(*msgp)->next;
(*msgp) = msg;
return msg;
}
/* Remove and destroy msg from queue */
static void golay_msg_destroy(gsc_t *gsc, gsc_msg_t *msg)
{
gsc_msg_t **msgp;
/* unlink */
msgp = &gsc->msg_list;
while ((*msgp) != msg)
msgp = &(*msgp)->next;
(*msgp) = msg->next;
/* destroy */
free(msg);
}
/* uncomment this for showing encoder tables ("<parity> <information>", LSB is the right most bit) */
//#define DEBUG_TABLE
static uint32_t golay_table[4096];
#define X22 0x00400000
#define X11 0x00000800
#define MASK12 0xfffff800
#define GEN_GOL 0x00000c75
/* generate golay encoding table. the redundancy is shifted 12 bits */
void init_golay(void)
{
uint32_t syndrome, aux;
int data;
for (data = 0; data < 4096; data++) {
syndrome = data << 11;
/* calculate syndrome */
aux = X22;
if (syndrome >= X11) {
while (syndrome & MASK12) {
while (!(aux & syndrome))
aux = aux >> 1;
syndrome ^= (aux / X11) * GEN_GOL;
}
}
golay_table[data] = data | (syndrome << 12);
#ifdef DEBUG_TABLE
printf("Golay %4d: ", data);
for (int i = 22; i >= 0; i--) {
if (i == 11)
printf(" ");
printf("%d", (golay_table[data] >> i) & 1);
}
printf("\n");
#endif
}
}
static uint16_t bch_table[128];
#define X14 0x4000
#define X8 0x0100
#define MASK7 0xff00
#define GEN_BCH 0x00000117
/* generate bch encoding table. the redundancy is shifted 7 bits */
void init_bch(void)
{
uint16_t syndrome, aux;
int data;
for (data = 0; data < 128; data++) {
syndrome = data << 8;
/* calculate syndrome */
aux = X14;
if (syndrome >= X8) {
while (syndrome & MASK7) {
while (!(aux & syndrome))
aux = aux >> 1;
syndrome ^= (aux / X8) * GEN_BCH;
}
}
bch_table[data] = data | (syndrome << 7);
#ifdef DEBUG_TABLE
printf("BCH %3d: ", data);
for (int i = 14; i >= 0; i--) {
if (i == 6)
printf(" ");
printf("%d", (bch_table[data] >> i) & 1);
}
printf("\n");
#endif
}
}
static inline uint32_t calc_golay(uint16_t data)
{
return golay_table[data & 0xfff];
}
static inline uint16_t calc_bch(uint16_t data)
{
return bch_table[data & 0x7f];
}
static const uint16_t preamble_values[] = {
2030, 1628, 3198, 647, 191, 3315, 1949, 2540, 1560, 2335,
};
static const uint32_t start_code = 713;
/* Rep. 900-2 Table VI */
static const uint16_t word1s[50] = {
721, 2731, 2952, 1387, 1578, 1708, 2650, 1747, 2580, 1376,
2692, 696, 1667, 3800, 3552, 3424, 1384, 3595, 876, 3124,
2285, 2608, 899, 3684, 3129, 2124, 1287, 2616, 1647, 3216,
375, 1232, 2824, 1840, 408, 3127, 3387, 882, 3468, 3267,
1575, 3463, 3152, 2572, 1252, 2592, 1552, 835, 1440, 160,
};
/* Rep. 900-2 Table VII (left column) */
static char encode_alpha(char c)
{
if (c >= 'a' && c <= 'z')
c = c - 'a' + 'A';
switch (c) {
case 0x0a:
case 0x0d:
PDEBUG(DGOLAY, DEBUG_DEBUG, " -> CR/LF character.\n");
c = 0x3c;
break;
case '{':
PDEBUG(DGOLAY, DEBUG_DEBUG, " -> '%c' character.\n", c);
c = 0x3b;
break;
case '}':
PDEBUG(DGOLAY, DEBUG_DEBUG, " -> '%c' character.\n", c);
c = 0x3d;
break;
case '\\':
PDEBUG(DGOLAY, DEBUG_DEBUG, " -> '%c' character.\n", c);
c = 0x20;
break;
default:
if (c < 0x20 || c > 0x5d) {
PDEBUG(DGOLAY, DEBUG_DEBUG, " -> ' ' character.\n");
c = 0x20;
} else {
PDEBUG(DGOLAY, DEBUG_DEBUG, " -> '%c' character.\n", c);
c = c - 0x20;
}
}
return c;
}
/* Rep. 900-2 Table VII (right columns) */
static char encode_numeric(char c)
{
switch (c) {
case 'u':
case 'U':
PDEBUG(DGOLAY, DEBUG_DEBUG, " -> 'U' character.\n");
c = 0xb;
break;
case ' ':
PDEBUG(DGOLAY, DEBUG_DEBUG, " -> '%c' character.\n", c);
c = 0xc;
break;
case '-':
PDEBUG(DGOLAY, DEBUG_DEBUG, " -> '%c' character.\n", c);
c = 0xd;
break;
case '=':
case '*':
PDEBUG(DGOLAY, DEBUG_DEBUG, " -> '*' character.\n");
c = 0xe;
break;
case 'a':
case 'A':
PDEBUG(DGOLAY, DEBUG_DEBUG, " -> 'A' character.\n");
c = 0xf0;
break;
case 'b':
case 'B':
PDEBUG(DGOLAY, DEBUG_DEBUG, " -> 'B' character.\n");
c = 0xf1;
break;
case 'c':
case 'C':
PDEBUG(DGOLAY, DEBUG_DEBUG, " -> 'C' character.\n");
c = 0xf2;
break;
case 'd':
case 'D':
PDEBUG(DGOLAY, DEBUG_DEBUG, " -> 'D' character.\n");
c = 0xf3;
break;
case 'e':
case 'E':
PDEBUG(DGOLAY, DEBUG_DEBUG, " -> 'E' character.\n");
c = 0xf4;
break;
case 'f':
case 'F':
PDEBUG(DGOLAY, DEBUG_DEBUG, " -> 'F' character.\n");
c = 0xf6;
break;
case 'g':
case 'G':
PDEBUG(DGOLAY, DEBUG_DEBUG, " -> 'G' character.\n");
c = 0xf7;
break;
case 'h':
case 'H':
PDEBUG(DGOLAY, DEBUG_DEBUG, " -> 'H' character.\n");
c = 0xf8;
break;
case 'j':
case 'J':
PDEBUG(DGOLAY, DEBUG_DEBUG, " -> 'J' character.\n");
c = 0xf9;
break;
case 'l':
case 'L':
PDEBUG(DGOLAY, DEBUG_DEBUG, " -> 'L' character.\n");
c = 0xfb;
break;
case 'n':
case 'N':
PDEBUG(DGOLAY, DEBUG_DEBUG, " -> 'N' character.\n");
c = 0xfc;
break;
case 'p':
case 'P':
PDEBUG(DGOLAY, DEBUG_DEBUG, " -> 'P' character.\n");
c = 0xfd;
break;
case 'r':
case 'R':
PDEBUG(DGOLAY, DEBUG_DEBUG, " -> 'r' character.\n");
c = 0xfe;
break;
default:
if (c >= '0' && c <= '9') {
PDEBUG(DGOLAY, DEBUG_DEBUG, " -> '%c' character.\n", c);
c = c - '0';
} else {
PDEBUG(DGOLAY, DEBUG_DEBUG, " -> ' ' character.\n");
c = 0xc;
}
}
return c;
}
static int encode_address(const char *code, int *preamble, uint16_t *word1, uint16_t *word2)
{
static const uint16_t illegal_low[16] = { 0, 25, 51, 103, 206, 340, 363, 412, 445, 530, 642, 726, 782, 810, 825, 877 };
static const uint16_t illegal_high[7] = { 0, 292, 425, 584, 631, 841, 851 };
int idx, g0, g1, a0, a1, a2, ap0, ap, ap1, ap2, ap3, b1b0, b3b2, g1g0, a2a1a0;
int i;
for (i = 0; i < 7; i++) {
if (code[i] < '0' || code[i] > '9')
break;
}
if (code[i]) {
PDEBUG(DGOLAY, DEBUG_NOTICE, "Invalid functional address character. Only 0..9 are allowed.\n");
return -EINVAL;
}
idx = code[0] - '0';
g1 = code[1] - '0';
g0 = code[2] - '0';
a2 = code[3] - '0';
a1 = code[4] - '0';
a0 = code[5] - '0';
*preamble = (idx + g0) % 10;
ap = a2 * 200 + a1 * 20 + a0 * 2;
ap3 = ap / 1000;
ap2 = (ap / 100) % 10;
ap1 = (ap / 10) % 10;
ap0 = ap % 10;
b1b0 = (ap1 * 10 + ap0) / 2;
b3b2 = (ap3 * 10 + ap2);
g1g0 = (g1 * 10 + g0);
if (g1g0 >= 50) {
*word1 = word1s[g1g0 - 50];
*word2 = b3b2 * 100 + b1b0 + 50;
} else {
*word1 = word1s[g1g0];
*word2 = b3b2 * 100 + b1b0;
}
a2a1a0 = a2 * 100 + a1 * 10 + a0;
if (g1g0 < 50) {
for (i = 0; i < 16; i++) {
if (a2a1a0 == illegal_low[i])
break;
}
if (i < 16) {
PDEBUG(DGOLAY, DEBUG_NOTICE, "Functional address has invlid value '%03d' for last three characters.\n", a2a1a0);
return -EINVAL;
}
} else {
for (i = 0; i < 7; i++) {
if (a2a1a0 == illegal_high[i])
break;
}
if (i < 7) {
PDEBUG(DGOLAY, DEBUG_NOTICE, "Functional address has invlid value '%03d' for last three characters.\n", a2a1a0);
return -EINVAL;
}
}
return 0;
}
static inline void queue_reset(gsc_t *gsc)
{
gsc->bit_index = 0;
gsc->bit_num = 0;
gsc->bit_overflow = 0;
}
static inline void queue_bit(gsc_t *gsc, int bit)
{
if (gsc->bit_num == sizeof(gsc->bit))
gsc->bit_overflow = 1;
if (gsc->bit_overflow) {
gsc->bit_num++;
return;
}
gsc->bit[gsc->bit_num++] = bit;
}
static inline void queue_dup(gsc_t *gsc, uint32_t data, int len)
{
int i;
for (i = 0; i < len; i++) {
queue_bit(gsc, (data >> i) & 1);
queue_bit(gsc, (data >> i) & 1);
}
}
static inline void queue_comma(gsc_t *gsc, int bits, uint8_t polarity)
{
int i;
for (i = 0; i < bits; i++) {
queue_bit(gsc, polarity);
polarity = !polarity;
}
}
static int queue_batch(gsc_t *gsc, const char *address, int force_type, const char *message)
{
int type;
int preamble;
uint16_t word1, word2;
uint8_t function;
uint32_t golay;
uint16_t bch[8];
uint8_t msg[12], digit, shifted, contbit, checksum;
int i, j, k;
int rc;
queue_reset(gsc);
/* check address length */
if (!address || strlen(address) != 7) {
PDEBUG(DGOLAY, DEBUG_NOTICE, "Invalid functional address '%s' size. Only 7 digits are allowed.\n", address);
return -EINVAL;
}
/* calculate address */
rc = encode_address(address, &preamble, &word1, &word2);
if (rc < 0)
return rc;
/* calculate function */
switch (address[6]) {
case '1': type = TYPE_VOICE; function = 0; break;
case '2': type = TYPE_VOICE; function = 1; break;
case '3': type = TYPE_VOICE; function = 2; break;
case '4': type = TYPE_VOICE; function = 3; break;
case '5': type = TYPE_ALPHA; function = 0; break;
case '6': type = TYPE_ALPHA; function = 1; break;
case '7': type = TYPE_ALPHA; function = 2; break;
case '8': type = TYPE_ALPHA; function = 3; break;
case '9': type = TYPE_TONE; function = 0; break;
case '0': type = TYPE_TONE; function = 1; break;
default:
PDEBUG(DGOLAY, DEBUG_NOTICE, "Illegal function suffix '%c' in last address digit.\n", address[6]);
return -EINVAL;
}
/* override type */
if (force_type >= 0) {
type = force_type;
PDEBUG(DGOLAY, DEBUG_INFO, "Overriding message type as defined by sender.\n");
}
if (type == TYPE_ALPHA || type == TYPE_NUMERIC)
PDEBUG(DGOLAY, DEBUG_INFO, "Coding text message for functional address '%s' and message '%s'.\n", address, message);
else
PDEBUG(DGOLAY, DEBUG_INFO, "Coding tone only message for functional address %s.\n", address);
/* encode preamble and store */
PDEBUG(DGOLAY, DEBUG_DEBUG, "Encoding preamble '%d'.\n", preamble);
golay = calc_golay(preamble_values[preamble]);
queue_comma(gsc, 28, golay & 1);
for (i = 0; i < 18; i++) {
queue_dup(gsc, golay, 23);
}
/* encode start code and store */
PDEBUG(DGOLAY, DEBUG_DEBUG, "Encoding start code.\n");
golay = calc_golay(start_code);
queue_comma(gsc, 28, golay & 1);
queue_dup(gsc, golay, 23);
golay ^= 0x7fffff;
queue_bit(gsc, (golay & 1) ^ 1);
queue_dup(gsc, golay, 23);
/* encode address and store */
PDEBUG(DGOLAY, DEBUG_DEBUG, "Encoding address words '%d' and '%d'.\n", word1, word2);
golay = calc_golay(word1);
if (function & 0x2)
golay ^= 0x7fffff;
queue_comma(gsc, 28, golay & 1);
queue_dup(gsc, golay, 23);
golay = calc_golay(word2);
if (function & 0x1)
golay ^= 0x7fffff;
queue_bit(gsc, (golay & 1) ^ 1);
queue_dup(gsc, golay, 23);
/* encode message */
if (type == TYPE_ALPHA) {
PDEBUG(DGOLAY, DEBUG_DEBUG, "Encoding %d alphanumeric digits.\n", (int)strlen(message));
for (i = 0; *message; i++) {
if (i == MAX_ADB) {
PDEBUG(DGOLAY, DEBUG_NOTICE, "Message overflows %d characters, cropping message.\n", MAX_ADB * 8);
}
for (j = 0; *message && j < 8; j++) {
msg[j] = encode_alpha(*message++);
}
/* fill empty characters with NULL */
while (j < 8)
msg[j++] = 0x3e;
/* 8 characters + continue-bit */
bch[0] = calc_bch((msg[0] | (msg[1] << 6)) & 0x7f);
bch[1] = calc_bch(((msg[1] >> 1) | (msg[2] << 5)) & 0x7f);
bch[2] = calc_bch(((msg[2] >> 2) | (msg[3] << 4)) & 0x7f);
bch[3] = calc_bch(((msg[3] >> 3) | (msg[4] << 3)) & 0x7f);
bch[4] = calc_bch(((msg[4] >> 4) | (msg[5] << 2)) & 0x7f);
bch[5] = calc_bch(((msg[5] >> 5) | (msg[6] << 1)) & 0x7f);
if (*message && i < MAX_ADB)
contbit = 1;
else
contbit = 0;
bch[6] = calc_bch((contbit << 6) | msg[7]);
/* checksum */
checksum = bch[0] + bch[1] + bch[2] + bch[3] + bch[4] + bch[5] + bch[6];
bch[7] = calc_bch(checksum & 0x7f);
/* store comma bit */
queue_bit(gsc, (bch[0] & 1) ^ 1); // inverted first bit
/* store interleaved bits */
for (j = 0; j < 15; j++) {
for (k = 0; k < 8; k++)
queue_bit(gsc, (bch[k] >> j) & 1);
}
}
}
if (type == TYPE_NUMERIC) {
PDEBUG(DGOLAY, DEBUG_DEBUG, "Encoding %d numeric digits.\n", (int)strlen(message));
shifted = 0;
for (i = 0; *message; i++) {
if (i == MAX_NDB) {
PDEBUG(DGOLAY, DEBUG_NOTICE, "Message overflows %d characters, cropping message.\n", MAX_NDB * 12);
}
for (j = 0; *message && j < 12; j++) {
/* get next digit or shifted digit */
if (shifted) {
digit = shifted & 0xf;
shifted = 0;
} else
digit = encode_numeric(*message);
/* if digit is extended, use the shifted code and later the digit itself */
if (digit > 0xf) {
shifted = digit;
msg[j] = digit >> 4;
} else {
msg[j] = digit;
message++;
}
}
/* fill empty digits with NULL */
while (j < 12)
msg[j++] = 0xa;
/* 8 digits + continue-bit */
bch[0] = calc_bch((msg[0] | (msg[1] << 4)) & 0x7f);
bch[1] = calc_bch(((msg[1] >> 3) | (msg[2] << 1) | (msg[3] << 5)) & 0x7f);
bch[2] = calc_bch(((msg[3] >> 2) | (msg[4] << 2) | (msg[5] << 6)) & 0x7f);
bch[3] = calc_bch(((msg[5] >> 1) | (msg[6] << 3)) & 0x7f);
bch[4] = calc_bch((msg[7] | (msg[8] << 4)) & 0x7f);
bch[5] = calc_bch(((msg[8] >> 3) | (msg[9] << 1) | (msg[10] << 5)) & 0x7f);
if (*message && i < MAX_NDB)
contbit = 1;
else
contbit = 0;
bch[6] = calc_bch((contbit << 6) | (msg[10] >> 2) | (msg[11] << 2));
/* checksum */
checksum = bch[0] + bch[1] + bch[2] + bch[3] + bch[4] + bch[5] + bch[6];
bch[7] = calc_bch(checksum & 0x7f);
/* store comma bit */
queue_bit(gsc, (bch[0] & 1) ^ 1); // inverted first bit
/* store interleaved bits */
for (j = 0; j < 15; j++) {
for (k = 0; k < 8; k++)
queue_bit(gsc, (bch[k] >> j) & 1);
}
}
}
/* encode comma after message and store */
PDEBUG(DGOLAY, DEBUG_DEBUG, "Encoding 'comma' sequence after message.\n");
queue_comma(gsc, 121 * 8, 1);
/* check overflow */
if (gsc->bit_overflow) {
PDEBUG(DGOLAY, DEBUG_ERROR, "Bit stream (%d bits) overflows bit buffer size (%d bits), please fix!\n", gsc->bit_num, (int)sizeof(gsc->bit));
return -EOVERFLOW;
}
return 0;
}
/* get next bit
*
* if there is no message, return -1, so that the transmitter is turned off.
*
* if there is a message, return next bit to be transmitted.
*
* if there is a message in the queue, encode message and return its first bit.
*/
int8_t get_bit(gsc_t *gsc)
{
gsc_msg_t *msg;
uint8_t bit;
int rc;
/* if currently transmiting message, send next bit */
if (gsc->bit_num) {
bit = gsc->bit[gsc->bit_index++];
if (gsc->bit_index == gsc->bit_num) {
queue_reset(gsc);
PDEBUG(DGOLAY, DEBUG_INFO, "Done transmitting message.\n");
}
return bit;
}
next_msg:
msg = gsc->msg_list;
/* no message pending, turn transmitter off */
if (!msg)
return -1;
/* encode first message in queue */
rc = queue_batch(gsc, msg->address, msg->force_type, msg->data);
if (rc >= 0)
PDEBUG(DGOLAY, DEBUG_INFO, "Transmitting message to address '%s'.\n", msg->address);
golay_msg_destroy(gsc, msg);
if (rc < 0)
goto next_msg;
/* return first bit */
bit = gsc->bit[gsc->bit_index++];
return bit;
}
void golay_msg_send(const char *text)
{
char buffer[strlen(text) + 1], *p = buffer, *address_string, *message;
gsc_t *gsc;
int force_type = -1;
strcpy(buffer, text);
address_string = strsep(&p, ",");
message = p;
if (message) {
if (message[0] == 'a' && message[1] == ',') {
force_type = TYPE_ALPHA;
message += 2;
} else
if (message[0] == 'n' && message[1] == ',') {
force_type = TYPE_NUMERIC;
message += 2;
}
} else
message = "";
gsc = (gsc_t *) sender_head;
golay_msg_create(gsc, address_string, message, force_type);
}
void call_down_clock(void)
{
}
/* Call control starts call towards paging network. */
int call_down_setup(int __attribute__((unused)) callref, const char *caller_id, enum number_type __attribute__((unused)) caller_type, const char *dialing)
{
char channel = '\0';
sender_t *sender;
gsc_t *gsc;
const char *address;
const char *message;
gsc_msg_t *msg;
/* find transmitter */
for (sender = sender_head; sender; sender = sender->next) {
/* skip channels that are different than requested */
if (channel && sender->kanal[0] != channel)
continue;
gsc = (gsc_t *) sender;
/* check if base station cannot transmit */
if (!gsc->tx)
continue;
break;
}
if (!sender) {
if (channel)
PDEBUG(DGOLAY, DEBUG_NOTICE, "Cannot page, because given station not available, rejecting!\n");
else
PDEBUG(DGOLAY, DEBUG_NOTICE, "Cannot page, no trasmitting station available, rejecting!\n");
return -CAUSE_NOCHANNEL;
}
/* get address */
address = dialing;
/* get message */
if (caller_id[0])
message = caller_id;
else
message = gsc->default_message;
/* create call process to page station */
msg = golay_msg_create(gsc, address, message, -1);
if (!msg)
return -CAUSE_INVALNUMBER;
return -CAUSE_NORMAL;
}
void call_down_answer(int __attribute__((unused)) callref)
{
}
static void _release(int __attribute__((unused)) callref, int __attribute__((unused)) cause)
{
PDEBUG(DGOLAY, DEBUG_INFO, "Call has been disconnected by network.\n");
}
void call_down_disconnect(int callref, int cause)
{
_release(callref, cause);
call_up_release(callref, cause);
}
/* Call control releases call toward mobile station. */
void call_down_release(int callref, int cause)
{
_release(callref, cause);
}
/* Receive audio from call instance. */
void call_down_audio(int __attribute__((unused)) callref, uint16_t __attribute__((unused)) sequence, uint32_t __attribute__((unused)) timestamp, uint32_t __attribute__((unused)) ssrc, sample_t __attribute__((unused)) *samples, int __attribute__((unused)) count)
{
}
void dump_info(void) {}

54
src/golay/golay.h Normal file
View File

@ -0,0 +1,54 @@
#include "../libmobile/sender.h"
#define TYPE_TONE 0 /* TONE only */
#define TYPE_VOICE 1 /* TONE + VOICE */
#define TYPE_ALPHA 2 /* TONE + DATA */
#define TYPE_NUMERIC 3 /* TONE + DATA */
#define MAX_ADB 10 /* 80 characters */
#define MAX_NDB 2 /* 24 digits */
/* instance of outgoing message */
typedef struct gsc_msg {
struct gsc_msg *next;
char address[8]; /* 7 digits + EOL */
int force_type; /* override type from address digit 7 */
char data[256]; /* message to be transmitted */
} gsc_msg_t;
typedef struct gsc {
sender_t sender;
int tx;
gsc_msg_t *msg_list; /* queue of messages */
const char *default_message;
/* current trasmitting message */
uint8_t bit[4096];
int bit_num;
int bit_index; /* when playing out */
int bit_overflow;
/* dsp states */
double fsk_deviation; /* deviation of FSK signal on sound card */
double fsk_polarity; /* polarity of FSK signal (-1.0 = bit '1' is down) */
sample_t fsk_ramp_up[256]; /* samples of upward ramp shape */
sample_t fsk_ramp_down[256]; /* samples of downward ramp shape */
double fsk_bitduration; /* duration of a bit in samples */
double fsk_bitstep; /* fraction of a bit each sample */
sample_t *fsk_tx_buffer; /* tx buffer for one data block */
int fsk_tx_buffer_size; /* size of tx buffer (in samples) */
int fsk_tx_buffer_length; /* usage of buffer (in samples) */
int fsk_tx_buffer_pos; /* current position sending buffer */
double fsk_tx_phase; /* current bit position */
uint8_t fsk_tx_lastbit; /* last bit of last message, to correctly ramp */
} gsc_t;
int golay_create(const char *kanal, double frequency, const char *device, int use_sdr, int samplerate, double rx_gain, double tx_gain, double deviation, double polarity, const char *message, const char *write_rx_wave, const char *write_tx_wave, const char *read_rx_wave, const char *read_tx_wave, int loopback);
void golay_destroy(sender_t *sender);
void init_golay(void);
void init_bch(void);
int8_t get_bit(gsc_t *gsc);
void golay_msg_send(const char *buffer);

6
src/golay/image.c Normal file
View File

@ -0,0 +1,6 @@
#include <stdio.h>
const char *aaimage[] = {
NULL
};

302
src/golay/main.c Normal file
View File

@ -0,0 +1,302 @@
/* Golay/GSC pager 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 <stdint.h>
#include <stdlib.h>
#include <unistd.h>
#include <string.h>
#include <errno.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include "../libsample/sample.h"
#include "../libdebug/debug.h"
#include "../libmobile/call.h"
#include "../libmobile/main_mobile.h"
#include "../liboptions/options.h"
#include "../libfm/fm.h"
#include "../amps/tones.h"
#include "../amps/noanswer.h"
#include "../amps/outoforder.h"
#include "../amps/invalidnumber.h"
#include "../amps/congestion.h"
#include "golay.h"
#define MSG_SEND "/tmp/golay_msg_send"
#define MSG_RECEIVED "/tmp/golay_msg_received"
static int msg_send_fd = -1;
static int tx = 0; /* we transmit */
static int rx = 0; /* we receive */
static double deviation = 4500; /* WB confirmed by an email: POCSAG and GSC have same deviation of +-4.5 kHz. */
static int deviation_given = 0;
static double polarity = 1;
static int polarity_given = 0;
static const char *message = "1234";
void print_help(const char *arg0)
{
main_mobile_print_help(arg0, "| -k 462.900 | -k <MHz> ");
/* - - */
printf(" -T --tx\n");
printf(" Transmit GSC signal on given channel, to page a receiver. (default)\n");
printf(" -R --rx\n");
printf(" Receive GSC signal on given channel, so we are the receiver.\n");
printf(" If none of the options -T nor -R is given, only transmitter is enabled.\n");
printf(" -D --deviation wide | 4.5 | narrow | 1.0 | <other KHz>\n"); /* NB confirmed by IQ data from signal-id-wiki */
printf(" Choose deviation of FFSK signal (default %.0f KHz).\n", deviation / 1000.0);
printf(" -P --polarity -1 | nagative | 1 | positive\n");
printf(" Choose polarity of FFSK signal. 'positive' means that a binary 1 uses\n");
printf(" positive and a binary 0 negative deviation. (default %s KHz).\n", (polarity < 0) ? "negative" : "positive");
printf(" -M --message \"...\"\n");
printf(" Send this message, if no caller ID was given or if built-in console\n");
printf(" is used. (default \"%s\").\n", message);
printf("\n");
printf("File: %s\n", MSG_SEND);
printf(" Write \"<address>[,message]\" to it, to send a default message.\n");
printf(" Write \"<address>,n,message\" to it, to send a numeric message.\n");
printf(" Write \"<address>,a,message\" to it, to send an alphanumeric message.\n");
printf("\n");
printf("By default, an alphanumic message is sent, if last digit of the functional\n");
printf("address is 5..8. Otherwise a tone only message is sent.\n");
printf("\n");
printf("A numeric message can have up to 24 digits, they are: 0123456789U-* and space\n");
printf("Also 'shifted' digits can be sent using two digits, they are: ABCDEFGHJLNPR\n");
printf("\n");
printf("An aplhanumeric message can have up to 80 digits, sent upper case only.\n");
main_mobile_print_station_id();
main_mobile_print_hotkeys();
}
static void add_options(void)
{
main_mobile_add_options();
option_add('T', "tx", 0);
option_add('R', "rx", 0);
option_add('D', "deviation", 1);
option_add('P', "polarity", 1);
option_add('M', "message", 1);
}
static int handle_options(int short_option, int argi, char **argv)
{
switch (short_option) {
case 'T':
tx = 1;
break;
case 'R':
rx = 1;
break;
case 'D':
if (argv[argi][0] == 'n' || argv[argi][0] == 'N')
deviation = 1000.0;
else if (argv[argi][0] == 'w' || argv[argi][0] == 'W')
deviation = 4500.0;
else
deviation = atof(argv[argi]) * 1000.0;
if (deviation < 1000.0) {
fprintf(stderr, "Given deviation is too low, use higher deviation.\n");
return -EINVAL;
}
if (deviation > 10000.0) {
fprintf(stderr, "Given deviation is too high, use lower deviation.\n");
return -EINVAL;
}
deviation_given = 1;
break;
case 'P':
if (argv[argi][0] == 'n' || argv[argi][0] == 'N')
polarity = -1.0;
else if (argv[argi][0] == 'p' || argv[argi][0] == 'P')
polarity = 1.0;
else if (atoi(argv[argi]) == -1)
polarity = -1.0;
else if (atoi(argv[argi]) == 1)
polarity = 1.0;
else {
fprintf(stderr, "Given polarity is not positive nor negative, use '-h' for help.\n");
return -EINVAL;
}
polarity_given = 1;
break;
case 'M':
message = options_strdup(argv[argi++]);
break;
default:
return main_mobile_handle_options(short_option, argi, argv);
}
return 1;
}
static void myhandler(void)
{
static char buffer[256];
static int pos = 0, rc, i;
int space = sizeof(buffer) - pos;
rc = read(msg_send_fd, buffer + pos, space);
if (rc > 0) {
pos += rc;
if (pos == space) {
fprintf(stderr, "Message buffer overflow!\n");
pos = 0;
}
/* check for end of line */
for (i = 0; i < pos; i++) {
if (buffer[i] == '\r' || buffer[i] == '\n')
break;
}
/* send msg */
if (i < pos) {
buffer[i] = '\0';
pos = 0;
if (tx)
golay_msg_send(buffer);
else
PDEBUG(DGOLAY, DEBUG_ERROR, "Failed to send message, transmitter is not enabled!\n");
}
}
}
static const struct number_lengths number_lengths[] = {
{ 7, "functional address" },
{ 0, NULL }
};
int main(int argc, char *argv[])
{
int rc, argi;
const char *station_id = "";
int i;
double frequency;
/* GSC does not use emphasis, so disable it */
uses_emphasis = 0;
/* init common tones */
init_tones();
init_outoforder();
init_noanswer();
init_invalidnumber();
init_congestion();
/* init coding tables */
init_golay();
init_bch();
/* init mobile interface */
main_mobile_init("0123456789", number_lengths, NULL, NULL);
/* handle options / config file */
add_options();
rc = options_config_file(argc, argv, "~/.osmocom/analog/golay.conf", handle_options);
if (rc < 0)
return 0;
argi = options_command_line(argc, argv, handle_options);
if (argi <= 0)
return argi;
if (argi < argc) {
station_id = argv[argi];
rc = main_mobile_number_ask(station_id, "functional address");
if (rc)
return rc;
}
if (!num_kanal) {
printf("No channel is specified, Use '-k <MHz>' to define frequency.\n\n");
print_help(argv[0]);
return 0;
}
if (use_sdr) {
/* set device */
for (i = 0; i < num_kanal; i++)
dsp_device[i] = "sdr";
num_device = num_kanal;
}
if (num_kanal == 1 && num_device == 0)
num_device = 1; /* use default */
if (num_kanal != num_device) {
fprintf(stderr, "You need to specify as many sound devices as you have channels.\n");
goto fail;
}
/* TX is default */
if (!tx && !rx)
tx = 1;
if (rx) {
fprintf(stderr, "Sorry, but RX is not yet supported and maybe never will.\n");
goto fail;
}
/* TX & RX if loopback */
if (loopback)
tx = rx = 1;
/* create pipe for message sendy */
unlink(MSG_SEND);
rc = mkfifo(MSG_SEND, 0666);
if (rc < 0) {
fprintf(stderr, "Failed to create mwaaage send FIFO '%s'!\n", MSG_SEND);
goto fail;
} else {
msg_send_fd = open(MSG_SEND, O_RDONLY | O_NONBLOCK);
if (msg_send_fd < 0) {
fprintf(stderr, "Failed to open mwaaage send FIFO! '%s'\n", MSG_SEND);
goto fail;
}
}
/* inits */
fm_init(fast_math);
/* create transceiver instance */
for (i = 0; i < num_kanal; i++) {
frequency = atof(kanal[i]) * 1e6;
rc = golay_create(kanal[i], frequency, dsp_device[i], use_sdr, dsp_samplerate, rx_gain, tx_gain, deviation, polarity, message, write_rx_wave, write_tx_wave, read_rx_wave, read_tx_wave, loopback);
if (rc < 0) {
fprintf(stderr, "Failed to create \"Sender\" instance. Quitting!\n");
goto fail;
}
printf("Base station ready, please tune transmitter (or receiver) to %.4f MHz\n", frequency / 1e6);
}
main_mobile_loop("golay", &quit, myhandler, station_id);
fail:
/* pipe */
if (msg_send_fd > 0)
close(msg_send_fd);
unlink(MSG_SEND);
/* destroy transceiver instance */
while(sender_head)
golay_destroy(sender_head);
/* exits */
fm_exit();
options_free();
return 0;
}

View File

@ -276,7 +276,7 @@ static int generate_tone(imts_t *imts, sample_t *samples, int length)
void sender_send(sender_t *sender, sample_t *samples, uint8_t *power, int length)
{
imts_t *imts = (imts_t *) sender;
int count;
int count, input_num;
memset(power, 1, length);
@ -296,7 +296,9 @@ again:
break;
case DSP_MODE_AUDIO:
memset(power, 1, length);
jitter_load(&imts->sender.dejitter, samples, length);
input_num = samplerate_upsample_input_num(&sender->srstate, length);
jitter_load(&sender->dejitter, samples, input_num);
samplerate_upsample(&sender->srstate, samples, input_num, samples, length);
if (imts->pre_emphasis)
pre_emphasis(&imts->estate, samples, length);
break;

View File

@ -1284,7 +1284,7 @@ void call_down_release(int callref, __attribute__((unused)) int cause)
}
/* Receive audio from call instance. */
void call_down_audio(int callref, sample_t *samples, int count)
void call_down_audio(int callref, uint16_t sequence, uint32_t timestamp, uint32_t ssrc, sample_t *samples, int count)
{
sender_t *sender;
imts_t *imts;
@ -1298,9 +1298,7 @@ void call_down_audio(int callref, sample_t *samples, int count)
return;
if (imts->dsp_mode == DSP_MODE_AUDIO) {
sample_t up[(int)((double)count * imts->sender.srstate.factor + 0.5) + 10];
count = samplerate_upsample(&imts->sender.srstate, samples, count, up);
jitter_save(&imts->sender.dejitter, up, count);
jitter_save(&imts->sender.dejitter, samples, count, 1, sequence, timestamp, ssrc);
}
}

View File

@ -113,7 +113,7 @@ int dsp_init_sender(jolly_t *jolly, int nbfm, double squelch_db, int repeater)
/* repeater */
jolly->repeater = repeater;
jolly->repeater_max = (int)((double)jolly->sender.samplerate * REPEATER_TIME);
rc = jitter_create(&jolly->repeater_dejitter, jolly->sender.samplerate / 5);
rc = jitter_create(&jolly->repeater_dejitter, "repeater", jolly->sender.samplerate, sizeof(sample_t), 0.050, 0.500, JITTER_FLAG_NONE);
if (rc < 0) {
PDEBUG(DDSP, DEBUG_ERROR, "Failed to create and init repeater buffer!\n");
goto error;
@ -319,7 +319,7 @@ void sender_receive(sender_t *sender, sample_t *samples, int length, double rf_l
/* if repeater mode, store sample in jitter buffer */
if (jolly->repeater)
jitter_save(&jolly->repeater_dejitter, samples, length);
jitter_save(&jolly->repeater_dejitter, samples, length, 0, 0, 0, 0);
/* downsample, decode DTMF */
count = samplerate_downsample(&jolly->sender.srstate, samples, length);
@ -346,7 +346,7 @@ void sender_receive(sender_t *sender, sample_t *samples, int length, double rf_l
void sender_send(sender_t *sender, sample_t *samples, uint8_t *power, int length)
{
jolly_t *jolly = (jolly_t *) sender;
int count;
int count, input_num;
switch (jolly->state) {
case STATE_IDLE:
@ -368,7 +368,9 @@ void sender_send(sender_t *sender, sample_t *samples, uint8_t *power, int length
case STATE_CALL:
case STATE_CALL_DIALING:
memset(power, 1, length);
jitter_load(&jolly->sender.dejitter, samples, length);
input_num = samplerate_upsample_input_num(&sender->srstate, length);
jitter_load(&sender->dejitter, samples, input_num);
samplerate_upsample(&sender->srstate, samples, input_num, samples, length);
break;
case STATE_OUT_VERIFY:
case STATE_IN_PAGING:

View File

@ -594,7 +594,7 @@ void call_down_release(int callref, __attribute__((unused)) int cause)
}
/* Receive audio from call instance. */
void call_down_audio(int callref, sample_t *samples, int count)
void call_down_audio(int callref, uint16_t sequence, uint32_t timestamp, uint32_t ssrc, sample_t *samples, int count)
{
sender_t *sender;
jolly_t *jolly;
@ -607,11 +607,8 @@ void call_down_audio(int callref, sample_t *samples, int count)
if (!sender)
return;
if (jolly->state == STATE_CALL || jolly->state == STATE_CALL_DIALING) {
sample_t up[(int)((double)count * jolly->sender.srstate.factor + 0.5) + 10];
count = samplerate_upsample(&jolly->sender.srstate, samples, count, up);
jitter_save(&jolly->sender.dejitter, up, count);
}
if (jolly->state == STATE_CALL || jolly->state == STATE_CALL_DIALING)
jitter_save(&jolly->sender.dejitter, samples, count, 1, sequence, timestamp, ssrc);
}
void call_down_clock(void) {}

View File

@ -11072,7 +11072,7 @@ int init_voice(int samplerate)
{
samplerate_t srstate;
sample_t spl_in[CHUNK], *spl_out;
int i, s, j, chunk, count;
int i, s, j, chunk, count, output_num;
int rc;
jolly_voice.spl[0] = (sample_t *)digit_0;
@ -11109,7 +11109,8 @@ int init_voice(int samplerate)
}
for (i = 0; i < 13; i++) {
spl_out = calloc(((double)jolly_voice.size[i] * srstate.factor + 0.5) + 10, sizeof(*spl_out));
output_num = samplerate_upsample_output_num(&srstate, jolly_voice.size[i]);
spl_out = calloc(output_num, sizeof(*spl_out));
count = 0;
for (s = 0; s < jolly_voice.size[i]; s += CHUNK) {
chunk = jolly_voice.size[i] - s;
@ -11117,7 +11118,8 @@ int init_voice(int samplerate)
chunk = CHUNK;
for (j = 0; j < chunk; j++)
spl_in[j] = (double)(((int16_t *)(jolly_voice.spl[i]))[s + j]) / 32767.0 * GAIN;
count += samplerate_upsample(&srstate, spl_in, chunk, spl_out + count);
samplerate_upsample(&srstate, spl_in, chunk, spl_out + count, output_num);
count += output_num;
}
jolly_voice.spl[i] = spl_out;
jolly_voice.size[i] = count;

View File

@ -57,6 +57,7 @@ struct debug_cat {
{ "jollycom", "\033[1;34m" },
{ "eurosignal", "\033[1;34m" },
{ "pocsag", "\033[1;34m" },
{ "golay", "\033[1;34m" },
{ "5-ton-folge", "\033[1;34m" },
{ "frame", "\033[0;36m" },
{ "call", "\033[0;37m" },
@ -93,12 +94,13 @@ struct debug_cat {
{ "uk0", "\033[1;34m" },
{ "ph", "\033[0;33m" },
{ "dcf77", "\033[1;34m" },
{ "jitter", "\033[0;36m" },
{ NULL, NULL }
};
int debuglevel = DEBUG_INFO;
int debug_date = 0;
uint64_t debug_mask = ~0;
uint64_t debug_mask[2] = { ~0, ~0 };
extern int num_kanal;
void (*clear_console_text)(void) = NULL;
@ -155,7 +157,7 @@ void _printdebug(const char *file, const char __attribute__((unused)) *function,
if (debuglevel > level)
return;
if (!(debug_mask & ((uint64_t)1 << cat)))
if (!(debug_mask[cat >> 6] & ((uint64_t)1 << (cat & 63))))
return;
lock_debug();
@ -289,7 +291,7 @@ int parse_debug_opt(const char *optarg)
return -EINVAL;
}
if (dstring)
debug_mask = 0;
memset(debug_mask, 0, sizeof(debug_mask));
while((p = strsep(&dstring, ","))) {
for (i = 0; debug_cat[i].name; i++) {
if (!strcasecmp(p, debug_cat[i].name))
@ -300,7 +302,7 @@ int parse_debug_opt(const char *optarg)
free(dup);
return -EINVAL;
}
debug_mask |= ((uint64_t)1 << i);
debug_mask[i >> 6] |= ((uint64_t)1 << (i & 63));
}
free(dup);

View File

@ -19,42 +19,45 @@
#define DJOLLY 12
#define DEURO 13
#define DPOCSAG 14
#define DFUENF 15
#define DFRAME 16
#define DCALL 17
#define DCC 18
#define DDB 19
#define DTRANS 20
#define DDMS 21
#define DSMS 22
#define DSDR 23
#define DUHD 24
#define DSOAPY 25
#define DWAVE 26
#define DRADIO 27
#define DAM791X 28
#define DUART 29
#define DDEVICE 30
#define DDATENKLO 31
#define DZEIT 32
#define DSIM1 33
#define DSIM2 34
#define DSIMI 35
#define DSIM7 36
#define DMTP2 37
#define DMTP3 38
#define DMUP 39
#define DROUTER 40
#define DSTDERR 41
#define DSS5 42
#define DISDN 43
#define DMISDN 44
#define DDSS1 45
#define DSIP 46
#define DTEL 47
#define DUK0 48
#define DPH 49
#define DDCF77 50
#define DGOLAY 15
#define DFUENF 16
#define DFRAME 17
#define DCALL 18
#define DCC 19
#define DDB 20
#define DTRANS 21
#define DDMS 22
#define DSMS 23
#define DSDR 24
#define DUHD 25
#define DSOAPY 26
#define DWAVE 27
#define DRADIO 28
#define DAM791X 29
#define DUART 30
#define DDEVICE 31
#define DDATENKLO 32
#define DZEIT 33
#define DSIM1 34
#define DSIM2 35
#define DSIMI 36
#define DSIM7 37
#define DMTP2 38
#define DMTP3 39
#define DMUP 40
#define DROUTER 41
#define DSTDERR 42
#define DSS5 43
#define DISDN 44
#define DMISDN 45
#define DDSS1 46
#define DSIP 47
#define DTEL 48
#define DUK0 49
#define DPH 50
#define DDCF77 51
#define DJITTER 52
//NOTE: increment mask array, if 127 is exceeded
void lock_debug(void);
void unlock_debug(void);

View File

@ -22,6 +22,7 @@
#include <string.h>
#include <math.h>
#include "../libsample/sample.h"
#include "../libdebug/debug.h"
#include "dtmf_decode.h"
//#define DEBUG
@ -40,22 +41,6 @@
static const char dtmf_digit[] = " 123A456B789C*0#D";
#ifdef DEBUG
const char *_debug_amplitude(double level)
{
static char text[42];
strcpy(text, " : ");
if (level > 1.0)
level = 1.0;
if (level < -1.0)
level = -1.0;
text[20 + (int)(level * 20)] = '*';
return text;
}
#endif
int dtmf_decode_init(dtmf_dec_t *dtmf, void *priv, void (*recv_digit)(void *priv, char digit, dtmf_meas_t *meas), int samplerate, double max_amplitude, double min_amplitude)
{
int rc;
@ -98,6 +83,12 @@ void dtmf_decode_exit(dtmf_dec_t *dtmf)
fm_demod_exit(&dtmf->demod_high);
}
void dtmf_decode_reset(dtmf_dec_t *dtmf)
{
dtmf->detected = 0;
dtmf->count = 0;
}
void dtmf_decode_filter(dtmf_dec_t *dtmf, sample_t *samples, int length, sample_t *frequency_low, sample_t *frequency_high, sample_t *amplitude_low, sample_t *amplitude_high)
{
sample_t I_low[length], Q_low[length];
@ -143,7 +134,7 @@ void dtmf_decode(dtmf_dec_t *dtmf, sample_t *samples, int length)
for (i = 0; i < length; i++) {
#ifdef DEBUG
// printf("%s %.5f\n", _debug_amplitude(samples[i]/2.0), samples[i]/2.0);
// printf("%s %.5f\n", debug_amplitude(samples[i]/2.0), samples[i]/2.0);
#endif
/* get frequency of low frequencies, correct amplitude drop at cutoff point */
f1 = frequency_low[i] + (DTMF_LOW_1 + DTMF_LOW_4) / 2.0;

View File

@ -30,6 +30,7 @@ typedef struct dtmf_dec {
int dtmf_decode_init(dtmf_dec_t *dtmf, void *priv, void (*recv_digit)(void *priv, char digit, dtmf_meas_t *meas), int samplerate, double max_amplitude, double min_amplitude);
void dtmf_decode_exit(dtmf_dec_t *dtmf);
void dtmf_decode_reset(dtmf_dec_t *dtmf);
void dtmf_decode(dtmf_dec_t *dtmf, sample_t *samples, int length);
void dtmf_decode_filter(dtmf_dec_t *dtmf, sample_t *samples, int length, sample_t *frequency_low, sample_t *frequency_high, sample_t *amplitude_low, sample_t *amplitude_high);

View File

@ -27,10 +27,6 @@
#define PEAK_DTMF_LOW 0.2818 /* -11 dBm, relative to 0 dBm level */
#define PEAK_DTMF_HIGH 0.3548 /* -9 dBm, relative to 0 dBm level */
#define DTMF_DURATION 0.100 /* duration in seconds */
static sample_t dsp_sine_dtmf_low[65536];
static sample_t dsp_sine_dtmf_high[65536];
void dtmf_encode_init(dtmf_enc_t *dtmf, int samplerate, double dBm_level)
{
@ -38,17 +34,15 @@ void dtmf_encode_init(dtmf_enc_t *dtmf, int samplerate, double dBm_level)
memset(dtmf, 0, sizeof(*dtmf));
dtmf->samplerate = samplerate;
dtmf->max = (int)((double)samplerate * DTMF_DURATION + 0.5);
// FIXME: do this globally and not per instance */
for (i = 0; i < 65536; i++) {
dsp_sine_dtmf_low[i] = sin((double)i / 65536.0 * 2.0 * PI) * PEAK_DTMF_LOW * dBm_level;
dsp_sine_dtmf_high[i] = sin((double)i / 65536.0 * 2.0 * PI) * PEAK_DTMF_HIGH * dBm_level;
dtmf->sine_low[i] = sin((double)i / 65536.0 * 2.0 * PI) * PEAK_DTMF_LOW * dBm_level;
dtmf->sine_high[i] = sin((double)i / 65536.0 * 2.0 * PI) * PEAK_DTMF_HIGH * dBm_level;
}
}
/* set dtmf tone */
void dtmf_encode_set_tone(dtmf_enc_t *dtmf, char tone)
int dtmf_encode_set_tone(dtmf_enc_t *dtmf, char tone, double on_duration, double off_duration)
{
double f1, f2;
@ -71,53 +65,66 @@ void dtmf_encode_set_tone(dtmf_enc_t *dtmf, char tone)
case'd':case 'D': f1 = 941.0; f2 = 1633.0; break;
default:
dtmf->tone = 0;
return;
return -1;
}
dtmf->tone = tone;
dtmf->pos = 0;
dtmf->on = (int)((double)dtmf->samplerate * on_duration);
dtmf->off = dtmf->on + (int)((double)dtmf->samplerate * off_duration);
dtmf->phaseshift65536[0] = 65536.0 / ((double)dtmf->samplerate / f1);
dtmf->phaseshift65536[1] = 65536.0 / ((double)dtmf->samplerate / f2);
dtmf->phase65536[0] = 0.0;
dtmf->phase65536[1] = 0.0;
return 0;
}
/* Generate audio stream from DTMF tone. Keep phase for next call of function. */
void dtmf_encode(dtmf_enc_t *dtmf, sample_t *samples, int length)
/* Generate audio stream from DTMF tone.
* Keep phase for next call of function.
* Stop, if tone has finished and return only the samples that were used.
*/
int dtmf_encode(dtmf_enc_t *dtmf, sample_t *samples, int length)
{
double *phaseshift, *phase;
int i, pos, max;
sample_t *sine_low, *sine_high;
int count = 0;
int i;
/* use silence, if no tone */
if (!dtmf->tone) {
memset(samples, 0, length * sizeof(*samples));
return;
}
/* if no tone */
if (!dtmf->tone)
return 0;
sine_low = dtmf->sine_low;
sine_high = dtmf->sine_high;
phaseshift = dtmf->phaseshift65536;
phase = dtmf->phase65536;
pos = dtmf->pos;
max = dtmf->max;
for (i = 0; i < length; i++) {
*samples++ = dsp_sine_dtmf_low[(uint16_t)phase[0]]
+ dsp_sine_dtmf_high[(uint16_t)phase[1]];
*samples++ = sine_low[(uint16_t)phase[0]]
+ sine_high[(uint16_t)phase[1]];
phase[0] += phaseshift[0];
if (phase[0] >= 65536)
phase[0] -= 65536;
if (phase[0] >= 65536.0)
phase[0] -= 65536.0;
phase[1] += phaseshift[1];
if (phase[1] >= 65536)
phase[1] -= 65536;
if (phase[1] >= 65536.0)
phase[1] -= 65536.0;
dtmf->pos++;
/* tone ends */
if (++pos == max) {
if (dtmf->pos == dtmf->on) {
phaseshift[0] = 0.0;
phaseshift[1] = 0.0;
phase[0] = 0.0;
phase[1] = 0.0;
}
/* pause ends */
if (dtmf->pos == dtmf->off) {
dtmf->tone = 0;
break;
}
}
length -= i;
count += i;
dtmf->pos = pos;
/* if tone ends, fill rest with silence */
if (length)
memset(samples, 0, length * sizeof(*samples));
return count;
}

View File

@ -2,13 +2,16 @@
typedef struct dtmf_enc {
int samplerate; /* samplerate */
char tone; /* current tone to be played */
int on, off; /* samples to turn on and afterwards off */
int pos; /* sample counter for tone */
int max; /* max number of samples for tone duration */
double phaseshift65536[2]; /* how much the phase of sine wave changes per sample */
double phase65536[2]; /* current phase */
sample_t sine_low[65536]; /* sine tables at individual levels */
sample_t sine_high[65536];
} dtmf_enc_t;
void dtmf_encode_init(dtmf_enc_t *dtmf, int samplerate, double dBm_level);
void dtmf_encode_set_tone(dtmf_enc_t *dtmf, char tone);
void dtmf_encode(dtmf_enc_t *dtmf, sample_t *samples, int length);
int dtmf_encode_set_tone(dtmf_enc_t *dtmf, char tone, double on_duration, double off_duration);
int dtmf_encode(dtmf_enc_t *dtmf, sample_t *samples, int length);

View File

@ -1,3 +1,6 @@
#ifndef _LIB_FM_H
#define _LIB_FM_H
#include "../libfilter/iir_filter.h"
int fm_init(int fast_math);
@ -38,3 +41,4 @@ void fm_demod_exit(fm_demod_t *demod);
void fm_demodulate_complex(fm_demod_t *demod, sample_t *frequency, int length, float *baseband, sample_t *I, sample_t *Q);
void fm_demodulate_real(fm_demod_t *demod, sample_t *frequency, int length, sample_t *baseband, sample_t *I, sample_t *Q);
#endif /* _LIB_FM_H */

View File

@ -29,6 +29,12 @@
#define PI M_PI
/* uncomment to see the modulated curve */
//#define DEBUG_MODULATOR
/* uncomment to see the shape of the filter */
//#define DEBUG_MODULATOR_SHAPE
/*
* fsk = instance of fsk modem
* inst = instance of user
@ -41,6 +47,7 @@
*/
int fsk_mod_init(fsk_mod_t *fsk, void *inst, int (*send_bit)(void *inst), int samplerate, double bitrate, double f0, double f1, double level, int ffsk, int filter)
{
double temp;
int i;
int rc;
@ -49,7 +56,7 @@ int fsk_mod_init(fsk_mod_t *fsk, void *inst, int (*send_bit)(void *inst), int sa
memset(fsk, 0, sizeof(*fsk));
/* gen sine table with deviation */
fsk->sin_tab = calloc(65536+16384, sizeof(*fsk->sin_tab));
fsk->sin_tab = calloc(65536, sizeof(*fsk->sin_tab));
if (!fsk->sin_tab) {
fprintf(stderr, "No mem!\n");
rc = -ENOMEM;
@ -59,7 +66,6 @@ int fsk_mod_init(fsk_mod_t *fsk, void *inst, int (*send_bit)(void *inst), int sa
fsk->sin_tab[i] = sin((double)i / 65536.0 * 2.0 * PI) * level;
fsk->inst = inst;
fsk->tx_bit = -1;
fsk->level = level;
fsk->send_bit = send_bit;
fsk->f0_deviation = (f0 - f1) / 2.0;
@ -72,12 +78,12 @@ int fsk_mod_init(fsk_mod_t *fsk, void *inst, int (*send_bit)(void *inst), int sa
fsk->high_bit = 0;
}
fsk->bits_per_sample = (double)bitrate / (double)samplerate;
PDEBUG(DDSP, DEBUG_DEBUG, "Bitduration of %.4f bits per sample @ %d.\n", fsk->bits_per_sample, samplerate);
fsk->bits65536_per_sample = (double)bitrate / (double)samplerate * 65536.0;
PDEBUG(DDSP, DEBUG_DEBUG, "Bitduration of %.4f bits per sample @ %d.\n", fsk->bits65536_per_sample / 65536.0, samplerate);
fsk->phaseshift65536[0] = f0 / (double)samplerate * 65536.0;
PDEBUG(DDSP, DEBUG_DEBUG, "F0 = %.0f Hz (phaseshift65536[0] = %.4f)\n", f0, fsk->phaseshift65536[0]);
fsk->phaseshift65536[1] = f1 / (double)samplerate * 65536.0;
PDEBUG(DDSP, DEBUG_DEBUG, "F0 = %.0f Hz (phaseshift65536[0] = %.4f)\n", f0, fsk->phaseshift65536[0]);
PDEBUG(DDSP, DEBUG_DEBUG, "F1 = %.0f Hz (phaseshift65536[1] = %.4f)\n", f1, fsk->phaseshift65536[1]);
/* use ffsk modulation, i.e. each bit has an integer number of
@ -86,6 +92,12 @@ int fsk_mod_init(fsk_mod_t *fsk, void *inst, int (*send_bit)(void *inst), int sa
if (ffsk) {
double waves;
if (filter) {
PDEBUG(DDSP, DEBUG_ERROR, "Cannot use FFSK with filter.\n");
rc = -EINVAL;
goto error;
}
PDEBUG(DDSP, DEBUG_DEBUG, "enable FFSK modulation mode\n");
fsk->ffsk = 1;
waves = (f0 / bitrate);
@ -100,22 +112,39 @@ int fsk_mod_init(fsk_mod_t *fsk, void *inst, int (*send_bit)(void *inst), int sa
abort();
}
fsk->cycles_per_bit65536[1] = waves * 65536.0;
} else {
fsk->cycles_per_bit65536[0] = f0 / bitrate * 65536.0;
fsk->cycles_per_bit65536[1] = f1 / bitrate * 65536.0;
}
PDEBUG(DDSP, DEBUG_DEBUG, "F0 = %.0f Hz (cycles_per_bit65536[0] = %.4f)\n", f0, fsk->cycles_per_bit65536[0]);
PDEBUG(DDSP, DEBUG_DEBUG, "F1 = %.0f Hz (cycles_per_bit65536[1] = %.4f)\n", f1, fsk->cycles_per_bit65536[1]);
/* if filter is enabled, add a band pass filter to smooth the spectrum of the tones
* the bandwidth is twice the difference between f0 and f1
*/
/* if filter is enabled, use a cosine shaped curve to change the phase each sample */
if (filter) {
double low = (f0 + f1) / 2.0 - fabs(f0 - f1);
double high = (f0 + f1) / 2.0 + fabs(f0 - f1);
fsk->phase_tab_0_1 = calloc(65536 + 65536, sizeof(*fsk->sin_tab));
if (!fsk->phase_tab_0_1) {
fprintf(stderr, "No mem!\n");
rc = -ENOMEM;
goto error;
}
fsk->phase_tab_1_0 = fsk->phase_tab_0_1 + 65536;
for (i = 0; i < 65536; i++) {
temp = cos((double)i / 65536.0 * PI) / 2 + 0.5; /* half cosine going from 1 to 0 */
fsk->phase_tab_0_1[i] = temp * fsk->phaseshift65536[0] + (1.0 - temp) * fsk->phaseshift65536[1];
fsk->phase_tab_1_0[i] = temp * fsk->phaseshift65536[1] + (1.0 - temp) * fsk->phaseshift65536[0];
#ifdef DEBUG_MODULATOR_SHAPE
fsk->phase_tab_0_1[i] = 1.0 - temp;
fsk->phase_tab_1_0[i] = temp;
#endif
}
PDEBUG(DDSP, DEBUG_DEBUG, "enable filter to smooth FSK transmission. (frequency rage %.0f .. %.0f)\n", low, high);
PDEBUG(DDSP, DEBUG_DEBUG, "Enable filter to smooth FSK transmission.\n");
fsk->filter = 1;
/* use fourth order (2 iter) filter, since it is as fast as second order (1 iter) filter */
iir_highpass_init(&fsk->lp[0], low, samplerate, 2);
iir_lowpass_init(&fsk->lp[1], high, samplerate, 2);
}
/* must reset, because bit states must be initialized */
fsk_mod_reset(fsk);
return 0;
error:
@ -132,6 +161,11 @@ void fsk_mod_cleanup(fsk_mod_t *fsk)
free(fsk->sin_tab);
fsk->sin_tab = NULL;
}
if (fsk->phase_tab_0_1) {
free(fsk->phase_tab_0_1);
fsk->phase_tab_0_1 = NULL;
fsk->phase_tab_1_0 = NULL;
}
}
/* modulate bits
@ -154,12 +188,16 @@ int fsk_mod_send(fsk_mod_t *fsk, sample_t *sample, int length, int add)
/* get next bit */
if (fsk->tx_bit < 0) {
next_bit:
fsk->tx_last_bit = fsk->tx_bit;
fsk->tx_bit = fsk->send_bit(fsk->inst);
#ifdef DEBUG_MODULATOR
printf("bit change to %d\n", fsk->tx_bit);
printf("bit change from %d to %d\n", fsk->tx_last_bit, fsk->tx_bit);
#endif
if (fsk->tx_bit < 0)
if (fsk->tx_bit < 0) {
fsk_mod_reset(fsk);
goto done;
}
fsk->tx_bit &= 1;
/* correct phase when changing bit */
if (fsk->ffsk) {
/* round phase to nearest zero crossing */
@ -167,40 +205,86 @@ next_bit:
phase = 32768.0;
else
phase = 0;
/* set phase according to current position in bit */
phase += fsk->tx_bitpos * fsk->cycles_per_bit65536[fsk->tx_bit & 1];
#ifdef DEBUG_MODULATOR
printf("phase %.3f bitpos=%.6f\n", phase, fsk->tx_bitpos);
printf("phase %.3f bitpos=%.6f\n", phase / 65536.0, fsk->tx_bitpos65536 / 65536.0);
#endif
}
if (!fsk->filter) {
/* change phase forward to the current bit position */
phase += fsk->tx_bitpos65536 / 65536.0 * fsk->cycles_per_bit65536[fsk->tx_bit];
if (phase >= 65536.0)
phase -= 65536.0;
}
}
/* modulate bit */
phaseshift = fsk->phaseshift65536[fsk->tx_bit & 1];
while (count < length && fsk->tx_bitpos < 1.0) {
if (add)
sample[count++] += fsk->sin_tab[(uint16_t)phase];
else
sample[count++] = fsk->sin_tab[(uint16_t)phase];
if (!fsk->filter || fsk->tx_last_bit < 0 || fsk->tx_last_bit == fsk->tx_bit) {
/* without filtering or when there is no bit change */
phaseshift = fsk->phaseshift65536[fsk->tx_bit];
while (count < length && fsk->tx_bitpos65536 < 65536.0) {
if (add)
sample[count++] += fsk->sin_tab[(uint16_t)phase];
else
sample[count++] = fsk->sin_tab[(uint16_t)phase];
#ifdef DEBUG_MODULATOR
printf("|%s|\n", debug_amplitude(fsk->sin_tab[(uint16_t)phase] / fsk->level));
printf("|%s| %d\n", debug_amplitude(fsk->sin_tab[(uint16_t)phase] / fsk->level), fsk->tx_bit);
#endif
phase += phaseshift;
if (phase >= 65536.0)
phase -= 65536.0;
fsk->tx_bitpos += fsk->bits_per_sample;
#ifdef DEBUG_MODULATOR_SHAPE
printf("|%s| %d\n", debug_amplitude(fsk->tx_bit), fsk->tx_bit);
#endif
phase += phaseshift;
if (phase >= 65536.0)
phase -= 65536.0;
fsk->tx_bitpos65536 += fsk->bits65536_per_sample;
}
} else if (fsk->tx_bit > fsk->tx_last_bit) {
/* with cosine shape filter, going from phase of 0 to phase of 1 */
while (count < length && fsk->tx_bitpos65536 < 65536.0) {
if (add)
sample[count++] += fsk->sin_tab[(uint16_t)phase];
else
sample[count++] = fsk->sin_tab[(uint16_t)phase];
#ifdef DEBUG_MODULATOR
printf("|%s| 0->1\n", debug_amplitude(fsk->sin_tab[(uint16_t)phase] / fsk->level));
#endif
#ifdef DEBUG_MODULATOR_SHAPE
printf("|%s|0->1\n", debug_amplitude(fsk->phase_tab_0_1[(uint16_t)fsk->tx_bitpos65536]));
#endif
phase += fsk->phase_tab_0_1[(uint16_t)fsk->tx_bitpos65536];
if (phase >= 65536.0)
phase -= 65536.0;
fsk->tx_bitpos65536 += fsk->bits65536_per_sample;
}
} else {
/* with cosine shape filter, going from phase of 1 to phase of 0 */
while (count < length && fsk->tx_bitpos65536 < 65536.0) {
if (add)
sample[count++] += fsk->sin_tab[(uint16_t)phase];
else
sample[count++] = fsk->sin_tab[(uint16_t)phase];
#ifdef DEBUG_MODULATOR
printf("|%s|1->0\n", debug_amplitude(fsk->sin_tab[(uint16_t)phase] / fsk->level));
#endif
#ifdef DEBUG_MODULATOR_SHAPE
printf("|%s|1->0\n", debug_amplitude(fsk->phase_tab_1_0[(uint16_t)fsk->tx_bitpos65536]));
#endif
phase += fsk->phase_tab_1_0[(uint16_t)fsk->tx_bitpos65536];
if (phase >= 65536.0)
phase -= 65536.0;
fsk->tx_bitpos65536 += fsk->bits65536_per_sample;
}
}
if (fsk->tx_bitpos >= 1.0) {
fsk->tx_bitpos -= 1.0;
if (fsk->tx_bitpos65536 >= 65536.0) {
fsk->tx_bitpos65536 -= 65536.0;
if (!fsk->filter) {
/* change phase back to the point when bit has changed */
phase -= fsk->tx_bitpos65536 / 65536.0 * fsk->cycles_per_bit65536[fsk->tx_bit];
if (phase < 0.0)
phase += 65536.0;
}
goto next_bit;
}
/* post filter */
if (fsk->filter) {
iir_process(&fsk->lp[0], sample, length);
iir_process(&fsk->lp[1], sample, length);
}
done:
fsk->tx_phase65536 = phase;
@ -208,11 +292,12 @@ done:
}
/* reset transmitter state, so we get a clean start */
void fsk_mod_tx_reset(fsk_mod_t *fsk)
void fsk_mod_reset(fsk_mod_t *fsk)
{
fsk->tx_phase65536 = 0;
fsk->tx_bitpos = 0;
fsk->tx_phase65536 = 0.0;
fsk->tx_bitpos65536 = 0.0;
fsk->tx_bit = -1;
fsk->tx_last_bit = -1;
}
/*

View File

@ -1,10 +1,15 @@
#ifndef _LIB_FSK_H
#define _LIB_FSK_H
#include "../libfm/fm.h"
typedef struct fsk_mod {
void *inst;
int (*send_bit)(void *inst);
double bits_per_sample; /* fraction of a bit per sample */
double bits65536_per_sample; /* fraction of a bit per sample */
double *sin_tab; /* sine table with correct peak level */
double *phase_tab_0_1; /* cosine shaped phase table (bit 0 to 1) */
double *phase_tab_1_0; /* cosine shaped phase table (bit 1 to 0) */
double phaseshift65536[2]; /* how much the phase of fsk synbol changes per sample */
double cycles_per_bit65536[2]; /* cycles of one bit */
double tx_phase65536; /* current transmit phase */
@ -14,9 +19,9 @@ typedef struct fsk_mod {
double f1_deviation;
int low_bit, high_bit; /* a low or high deviation means which bit? */
int tx_bit; /* current transmitting bit (-1 if not set) */
double tx_bitpos; /* current transmit position in bit */
int tx_last_bit; /* last transmitting bit (-1 if not set) */
double tx_bitpos65536; /* current transmit position in bit */
int filter; /* set, if filters are used */
iir_filter_t lp[2]; /* filter to smooth transmission spectrum */
} fsk_mod_t;
typedef struct fsk_demod {
@ -36,8 +41,9 @@ typedef struct fsk_demod {
int fsk_mod_init(fsk_mod_t *fsk, void *inst, int (*send_bit)(void *inst), int samplerate, double bitrate, double f0, double f1, double level, int coherent, int filter);
void fsk_mod_cleanup(fsk_mod_t *fsk);
int fsk_mod_send(fsk_mod_t *fsk, sample_t *sample, int length, int add);
void fsk_mod_tx_reset(fsk_mod_t *fsk);
void fsk_mod_reset(fsk_mod_t *fsk);
int fsk_demod_init(fsk_demod_t *fsk, void *inst, void (*receive_bit)(void *inst, int bit, double quality, double level), int samplerate, double bitrate, double f0, double f1, double bitadjust);
void fsk_demod_cleanup(fsk_demod_t *fsk);
void fsk_demod_receive(fsk_demod_t *fsk, sample_t *sample, int length);
#endif /* _LIB_FSK_H */

View File

@ -242,7 +242,7 @@ void g711_init(void)
g711_initialized = 1;
}
void g711_encode_alaw_flipped(uint8_t *src_data, int src_len, uint8_t **dst_data, int *dst_len)
void g711_encode_alaw_flipped(uint8_t *src_data, int src_len, uint8_t **dst_data, int *dst_len, void __attribute__((unused)) *priv)
{
int16_t *src = (int16_t *)src_data;
uint8_t *dst;
@ -262,7 +262,7 @@ void g711_encode_alaw_flipped(uint8_t *src_data, int src_len, uint8_t **dst_data
*dst_len = len;
}
void g711_encode_ulaw_flipped(uint8_t *src_data, int src_len, uint8_t **dst_data, int *dst_len)
void g711_encode_ulaw_flipped(uint8_t *src_data, int src_len, uint8_t **dst_data, int *dst_len, void __attribute__((unused)) *priv)
{
int16_t *src = (int16_t *)src_data;
uint8_t *dst;
@ -282,7 +282,7 @@ void g711_encode_ulaw_flipped(uint8_t *src_data, int src_len, uint8_t **dst_data
*dst_len = len;
}
void g711_decode_alaw_flipped(uint8_t *src_data, int src_len, uint8_t **dst_data, int *dst_len)
void g711_decode_alaw_flipped(uint8_t *src_data, int src_len, uint8_t **dst_data, int *dst_len, void __attribute__((unused)) *priv)
{
uint8_t *src = src_data;
int16_t *dst;
@ -302,7 +302,7 @@ void g711_decode_alaw_flipped(uint8_t *src_data, int src_len, uint8_t **dst_data
*dst_len = len * 2;
}
void g711_decode_ulaw_flipped(uint8_t *src_data, int src_len, uint8_t **dst_data, int *dst_len)
void g711_decode_ulaw_flipped(uint8_t *src_data, int src_len, uint8_t **dst_data, int *dst_len, void __attribute__((unused)) *priv)
{
uint8_t *src = src_data;
int16_t *dst;
@ -322,7 +322,7 @@ void g711_decode_ulaw_flipped(uint8_t *src_data, int src_len, uint8_t **dst_data
*dst_len = len * 2;
}
void g711_encode_alaw(uint8_t *src_data, int src_len, uint8_t **dst_data, int *dst_len)
void g711_encode_alaw(uint8_t *src_data, int src_len, uint8_t **dst_data, int *dst_len, void __attribute__((unused)) *priv)
{
int16_t *src = (int16_t *)src_data;
uint8_t *dst;
@ -342,7 +342,7 @@ void g711_encode_alaw(uint8_t *src_data, int src_len, uint8_t **dst_data, int *d
*dst_len = len;
}
void g711_encode_ulaw(uint8_t *src_data, int src_len, uint8_t **dst_data, int *dst_len)
void g711_encode_ulaw(uint8_t *src_data, int src_len, uint8_t **dst_data, int *dst_len, void __attribute__((unused)) *priv)
{
int16_t *src = (int16_t *)src_data;
uint8_t *dst;
@ -362,7 +362,7 @@ void g711_encode_ulaw(uint8_t *src_data, int src_len, uint8_t **dst_data, int *d
*dst_len = len;
}
void g711_decode_alaw(uint8_t *src_data, int src_len, uint8_t **dst_data, int *dst_len)
void g711_decode_alaw(uint8_t *src_data, int src_len, uint8_t **dst_data, int *dst_len, void __attribute__((unused)) *priv)
{
uint8_t *src = src_data;
int16_t *dst;
@ -382,7 +382,7 @@ void g711_decode_alaw(uint8_t *src_data, int src_len, uint8_t **dst_data, int *d
*dst_len = len * 2;
}
void g711_decode_ulaw(uint8_t *src_data, int src_len, uint8_t **dst_data, int *dst_len)
void g711_decode_ulaw(uint8_t *src_data, int src_len, uint8_t **dst_data, int *dst_len, void __attribute__((unused)) *priv)
{
uint8_t *src = src_data;
int16_t *dst;
@ -402,7 +402,7 @@ void g711_decode_ulaw(uint8_t *src_data, int src_len, uint8_t **dst_data, int *d
*dst_len = len * 2;
}
void g711_transcode_alaw_to_ulaw(uint8_t *src_data, int src_len, uint8_t **dst_data, int *dst_len)
void g711_transcode_alaw_to_ulaw(uint8_t *src_data, int src_len, uint8_t **dst_data, int *dst_len, void __attribute__((unused)) *priv)
{
uint8_t *src = src_data, *dst;
int len = src_len, i;
@ -421,7 +421,7 @@ void g711_transcode_alaw_to_ulaw(uint8_t *src_data, int src_len, uint8_t **dst_d
*dst_len = len;
}
void g711_transcode_alaw_flipped_to_ulaw(uint8_t *src_data, int src_len, uint8_t **dst_data, int *dst_len)
void g711_transcode_alaw_flipped_to_ulaw(uint8_t *src_data, int src_len, uint8_t **dst_data, int *dst_len, void __attribute__((unused)) *priv)
{
uint8_t *src = src_data, *dst;
int len = src_len, i;
@ -440,7 +440,7 @@ void g711_transcode_alaw_flipped_to_ulaw(uint8_t *src_data, int src_len, uint8_t
*dst_len = len;
}
void g711_transcode_alaw_to_ulaw_flipped(uint8_t *src_data, int src_len, uint8_t **dst_data, int *dst_len)
void g711_transcode_alaw_to_ulaw_flipped(uint8_t *src_data, int src_len, uint8_t **dst_data, int *dst_len, void __attribute__((unused)) *priv)
{
uint8_t *src = src_data, *dst;
int len = src_len, i;
@ -459,7 +459,7 @@ void g711_transcode_alaw_to_ulaw_flipped(uint8_t *src_data, int src_len, uint8_t
*dst_len = len;
}
void g711_transcode_ulaw_to_alaw(uint8_t *src_data, int src_len, uint8_t **dst_data, int *dst_len)
void g711_transcode_ulaw_to_alaw(uint8_t *src_data, int src_len, uint8_t **dst_data, int *dst_len, void __attribute__((unused)) *priv)
{
uint8_t *src = src_data, *dst;
int len = src_len, i;
@ -478,7 +478,7 @@ void g711_transcode_ulaw_to_alaw(uint8_t *src_data, int src_len, uint8_t **dst_d
*dst_len = len;
}
void g711_transcode_ulaw_flipped_to_alaw(uint8_t *src_data, int src_len, uint8_t **dst_data, int *dst_len)
void g711_transcode_ulaw_flipped_to_alaw(uint8_t *src_data, int src_len, uint8_t **dst_data, int *dst_len, void __attribute__((unused)) *priv)
{
uint8_t *src = src_data, *dst;
int len = src_len, i;
@ -497,7 +497,7 @@ void g711_transcode_ulaw_flipped_to_alaw(uint8_t *src_data, int src_len, uint8_t
*dst_len = len;
}
void g711_transcode_ulaw_to_alaw_flipped(uint8_t *src_data, int src_len, uint8_t **dst_data, int *dst_len)
void g711_transcode_ulaw_to_alaw_flipped(uint8_t *src_data, int src_len, uint8_t **dst_data, int *dst_len, void __attribute__((unused)) *priv)
{
uint8_t *src = src_data, *dst;
int len = src_len, i;
@ -516,7 +516,7 @@ void g711_transcode_ulaw_to_alaw_flipped(uint8_t *src_data, int src_len, uint8_t
*dst_len = len;
}
void g711_transcode_flipped(uint8_t *src_data, int src_len, uint8_t **dst_data, int *dst_len)
void g711_transcode_flipped(uint8_t *src_data, int src_len, uint8_t **dst_data, int *dst_len, void __attribute__((unused)) *priv)
{
uint8_t *src = src_data, *dst;
int len = src_len, i;

View File

@ -1,17 +1,17 @@
void g711_init(void);
void g711_encode_alaw_flipped(uint8_t *src_data, int src_len, uint8_t **dst_data, int *dst_len);
void g711_encode_ulaw_flipped(uint8_t *src_data, int src_len, uint8_t **dst_data, int *dst_len);
void g711_decode_alaw_flipped(uint8_t *src_data, int src_len, uint8_t **dst_data, int *dst_len);
void g711_decode_ulaw_flipped(uint8_t *src_data, int src_len, uint8_t **dst_data, int *dst_len);
void g711_encode_alaw(uint8_t *src_data, int src_len, uint8_t **dst_data, int *dst_len);
void g711_encode_ulaw(uint8_t *src_data, int src_len, uint8_t **dst_data, int *dst_len);
void g711_decode_alaw(uint8_t *src_data, int src_len, uint8_t **dst_data, int *dst_len);
void g711_decode_ulaw(uint8_t *src_data, int src_len, uint8_t **dst_data, int *dst_len);
void g711_transcode_alaw_to_ulaw(uint8_t *src_data, int src_len, uint8_t **dst_data, int *dst_len);
void g711_transcode_alaw_flipped_to_ulaw(uint8_t *src_data, int src_len, uint8_t **dst_data, int *dst_len);
void g711_transcode_alaw_to_ulaw_flipped(uint8_t *src_data, int src_len, uint8_t **dst_data, int *dst_len);
void g711_transcode_ulaw_to_alaw(uint8_t *src_data, int src_len, uint8_t **dst_data, int *dst_len);
void g711_transcode_ulaw_flipped_to_alaw(uint8_t *src_data, int src_len, uint8_t **dst_data, int *dst_len);
void g711_transcode_ulaw_to_alaw_flipped(uint8_t *src_data, int src_len, uint8_t **dst_data, int *dst_len);
void g711_transcode_flipped(uint8_t *src_data, int src_len, uint8_t **dst_data, int *dst_len);
void g711_encode_alaw_flipped(uint8_t *src_data, int src_len, uint8_t **dst_data, int *dst_len, void *priv);
void g711_encode_ulaw_flipped(uint8_t *src_data, int src_len, uint8_t **dst_data, int *dst_len, void *priv);
void g711_decode_alaw_flipped(uint8_t *src_data, int src_len, uint8_t **dst_data, int *dst_len, void *priv);
void g711_decode_ulaw_flipped(uint8_t *src_data, int src_len, uint8_t **dst_data, int *dst_len, void *priv);
void g711_encode_alaw(uint8_t *src_data, int src_len, uint8_t **dst_data, int *dst_len, void *priv);
void g711_encode_ulaw(uint8_t *src_data, int src_len, uint8_t **dst_data, int *dst_len, void *priv);
void g711_decode_alaw(uint8_t *src_data, int src_len, uint8_t **dst_data, int *dst_len, void *priv);
void g711_decode_ulaw(uint8_t *src_data, int src_len, uint8_t **dst_data, int *dst_len, void *priv);
void g711_transcode_alaw_to_ulaw(uint8_t *src_data, int src_len, uint8_t **dst_data, int *dst_len, void *priv);
void g711_transcode_alaw_flipped_to_ulaw(uint8_t *src_data, int src_len, uint8_t **dst_data, int *dst_len, void *priv);
void g711_transcode_alaw_to_ulaw_flipped(uint8_t *src_data, int src_len, uint8_t **dst_data, int *dst_len, void *priv);
void g711_transcode_ulaw_to_alaw(uint8_t *src_data, int src_len, uint8_t **dst_data, int *dst_len, void *priv);
void g711_transcode_ulaw_flipped_to_alaw(uint8_t *src_data, int src_len, uint8_t **dst_data, int *dst_len, void *priv);
void g711_transcode_ulaw_to_alaw_flipped(uint8_t *src_data, int src_len, uint8_t **dst_data, int *dst_len, void *priv);
void g711_transcode_flipped(uint8_t *src_data, int src_len, uint8_t **dst_data, int *dst_len, void *priv);

View File

@ -1,6 +1,6 @@
/* Jitter buffering functions
*
* (C) 2016 by Andreas Eversberg <jolly@eversberg.eu>
* (C) 2022 by Andreas Eversberg <jolly@eversberg.eu>
* All Rights Reserved
*
* This program is free software: you can redistribute it and/or modify
@ -17,6 +17,60 @@
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
/* How does it work:
*
* Storing:
*
* Each saved frame is sorted into the list of packages by their sequence
* number.
*
* The first packet will be stored with a delay of minimum jitter window size.
*
* Packets with the same sequence are dropped.
*
* Early packts that exceed maximum jitter window size cause jitter
* window to shift into the future.
*
* Late packets cause jitter window to shift into the past (allowing more
* delay). Minimum jitter window size is added also, to prevent subsequent
* packets from beeing late too.
*
* If no sequence is provided (autosequence), the sequence number is generated
* by a counter. Also the timestamp is generated by counting the length of each
* frame.
*
* If ssrc changes, the buffer is reset.
*
*
* Playout:
*
* The caller of the playout function can request any length of samples from
* the packet list. The packt's time stamp and the jitter window time stamp
* indicate what portion of a packet is already provided to the caller.
* Complete packet, sent to the caller, are removed.
*
* Missing packets are interpolated by repeating last 20ms of audio (optional)
* or by inserting zeroes (sample size > 1 byte) or by inserting 0xff (sample
* size = 1). In case of repeating audio, the number of turns are limited until
* buffer is reset to silence, if no frames are received for a certain time.
*
* Optionally the constant delay will be measured continuously and lowered if
* greater than minimum window size. (adaptive jitter buffer size)
*
* Note that the delay is measured with time stamp of frame, no matter what
* the length is. Length is an extra delay, but not considered here.
*
*
* Unlocking:
*
* If the buffer is created or reset, the buffer is locked, so no packets are
* stored. When the playout routine is called, the buffer is unlocked. This
* prevents from filling the buffer before playout is performed, which would
* cause high delay.
*
*/
#include <stdio.h>
#include <stdint.h>
#include <stdlib.h>
#include <string.h>
@ -26,35 +80,103 @@
#include "../libdebug/debug.h"
#include "jitter.h"
#define INITIAL_DELAY_INTERVAL 0.5
#define REPEAT_DELAY_INTERVAL 3.0
#define EXTRA_BUFFER 0.020 // 20 ms
#define EXTRA_TIMEOUT 0.500 // maximum time to repeat extrapolation buffer
/* uncomment to enable heavy debugging */
//#define HEAVY_DEBUG
static int unnamed_count = 1;
/* create jitter buffer */
int jitter_create(jitter_t *jitter, int length)
int jitter_create(jitter_t *jb, const char *name, double samplerate, int sample_size, double target_window_duration, double max_window_duration, uint32_t window_flags)
{
memset(jitter, 0, sizeof(*jitter));
jitter->spl = malloc(length * sizeof(sample_t));
if (!jitter->spl) {
PDEBUG(DDSP, DEBUG_ERROR, "No memory for jitter buffer.\n");
return -ENOMEM;
int rc = 0;
memset(jb, 0, sizeof(*jb));
jb->sample_duration = 1.0 / samplerate;
jb->sample_size = sample_size;
jb->target_window_size = (int)(samplerate * target_window_duration);
jb->max_window_size = (int)(samplerate * max_window_duration);
jb->window_flags = window_flags;
jb->extra_size = (int)(EXTRA_BUFFER * samplerate);
jb->extra_samples = calloc(sample_size, jb->extra_size);
if (!jb->extra_samples) {
PDEBUG(DJITTER, DEBUG_ERROR, "No memory for frame.\n");
rc = -ENOMEM;
goto error;
}
jitter->len = length;
jb->extra_timeout_max = (int)ceil(EXTRA_TIMEOUT / EXTRA_BUFFER);
jitter_reset(jitter);
/* optionally give a string to be show with the debug */
if (name && *name)
snprintf(jb->name, sizeof(jb->name) - 1, "(%s) ", name);
else
snprintf(jb->name, sizeof(jb->name) - 1, "(unnamed %d) ", unnamed_count++);
return 0;
jitter_reset(jb);
PDEBUG(DJITTER, DEBUG_INFO, "%sCreated jitter buffer. (samplerate=%.0f, target_window=%.0fms, max_window=%.0fms, flag:latency=%s flag:repeat=%s)\n", jb->name, samplerate, target_window_duration * 1000.0, max_window_duration * 1000.0, (window_flags & JITTER_FLAG_LATENCY) ? "true" : "false", (window_flags & JITTER_FLAG_REPEAT) ? "true" : "false");
error:
if (rc)
jitter_destroy(jb);
return rc;
}
void jitter_reset(jitter_t *jitter)
static void clear_extra_buffer(jitter_t *jb)
{
memset(jitter->spl, 0, jitter->len * sizeof(sample_t));
/* put write pointer ahead by half of the buffer length */
jitter->inptr = jitter->len / 2;
if (jb->sample_size == 1)
memset(jb->extra_samples, 0xff, jb->sample_size * jb->extra_size);
else
memset(jb->extra_samples, 0, jb->sample_size * jb->extra_size);
}
void jitter_destroy(jitter_t *jitter)
/* reset jitter buffer */
void jitter_reset(jitter_t *jb)
{
if (jitter->spl) {
free(jitter->spl);
jitter->spl = NULL;
jitter_frame_t *jf, *temp;
PDEBUG(DJITTER, DEBUG_INFO, "%sReset jitter buffer.\n", jb->name);
/* jitter buffer locked */
jb->unlocked = 0;
/* window becomes invalid */
jb->window_valid = 0;
/* remove all pending frames */
jf = jb->frame_list;
while(jf) {
temp = jf;
jf = jf->next;
free(temp);
}
jb->frame_list = NULL;
/* clear extrapolation buffer */
if (jb->extra_samples)
clear_extra_buffer(jb);
jb->extra_index = 0;
jb->extra_timeout_count = jb->extra_timeout_max; /* no data in buffer yet, so we set timeout condition */
/* delay measurement and reduction */
jb->delay_counter = 0.0;
jb->delay_interval = INITIAL_DELAY_INTERVAL;
jb->min_delay_value = -1;
}
void jitter_destroy(jitter_t *jb)
{
jitter_reset(jb);
PDEBUG(DJITTER, DEBUG_INFO, "%sDestroying jitter buffer.\n", jb->name);
if (jb->extra_samples) {
free(jb->extra_samples);
jb->extra_samples = NULL;
}
}
@ -62,64 +184,241 @@ void jitter_destroy(jitter_t *jitter)
*
* stop if buffer is completely filled
*/
void jitter_save(jitter_t *jb, sample_t *samples, int length)
void jitter_save(jitter_t *jb, void *samples, int length, int has_sequence, uint16_t sequence, uint32_t timestamp, uint32_t ssrc)
{
sample_t *spl;
int inptr, outptr, len, space;
int i;
jitter_frame_t *jf, **jfp;
int16_t offset_sequence;
int32_t offset_timestamp;
spl = jb->spl;
inptr = jb->inptr;
outptr = jb->outptr;
len = jb->len;
space = (outptr - inptr + len - 1) % len;
/* ignore frames until the buffer is unlocked by jitter_load() */
if (!jb->unlocked)
return;
if (space < length)
length = space;
for (i = 0; i < length; i++) {
spl[inptr++] = *samples++;
if (inptr == len)
inptr = 0;
/* omit frames with no data */
if (length < 1)
return;
/* generate sequence and timestamp automatically, if enabled */
if (!has_sequence) {
#ifdef DEBUG_JITTER
PDEBUG(DJITTER, DEBUG_DEBUG, "%sSave frame of %d samples (no seqence).\n", jb->name, length);
#endif
sequence = jb->next_sequence;
jb->next_sequence++;
timestamp = jb->next_timestamp;
jb->next_timestamp += length;
ssrc = jb->window_ssrc;
} else {
#ifdef HEAVY_DEBUG
PDEBUG(DJITTER, DEBUG_DEBUG, "%sSave frame of %d samples (seqence=%u timestamp=%u ssrc=0x%02x).\n", jb->name, length, sequence, timestamp, ssrc);
#endif
jb->next_sequence = sequence + 1;
jb->next_timestamp = timestamp + length;
}
jb->inptr = inptr;
/* first packet (with this ssrc) sets window size to target_window_size */
if (!jb->window_valid || jb->window_ssrc != ssrc) {
if (!jb->window_valid)
PDEBUG(DJITTER, DEBUG_DEBUG, "%s Initial frame after init or reset.\n", jb->name);
else
PDEBUG(DJITTER, DEBUG_DEBUG, "%s SSRC changed.\n", jb->name);
// NOTE: Reset must be called before finding the frame location below, because there will be no frame in list anymore!
jitter_reset(jb);
jb->unlocked = 1;
/* when using dynamic jitter buffer, we use half of the target delay */
if ((jb->window_flags & JITTER_FLAG_LATENCY)) {
jb->window_timestamp = timestamp - (uint32_t)jb->target_window_size / 2;
} else {
jb->window_timestamp = timestamp - (uint32_t)jb->target_window_size;
}
jb->window_valid = 1;
jb->window_ssrc = ssrc;
}
/* find location where to put frame into the list, depending on sequence number */
jfp = &jb->frame_list;
while(*jfp) {
offset_sequence = (int16_t)(sequence - (*jfp)->sequence);
/* found double entry */
if (offset_sequence == 0) {
PDEBUG(DJITTER, DEBUG_DEBUG, "%s Dropping double packet (sequence = %d)\n", jb->name, sequence);
return;
}
/* offset is negative, so we found the position to insert frame */
if (offset_sequence < 0)
break;
jfp = &((*jfp)->next);
}
offset_timestamp = timestamp - jb->window_timestamp;
#ifdef HEAVY_DEBUG
PDEBUG(DJITTER, DEBUG_DEBUG, "%sFrame has offset of %.0fms in jitter buffer.\n", jb->name, (double)offset_timestamp * jb->sample_duration * 1000.0);
#endif
/* measure delay */
if (jb->min_delay_value < 0 || offset_timestamp < jb->min_delay_value)
jb->min_delay_value = offset_timestamp;
/* if frame is too early (delay ceases), shift window to the future */
if (offset_timestamp > jb->max_window_size) {
if ((jb->window_flags & JITTER_FLAG_LATENCY)) {
PDEBUG(DJITTER, DEBUG_DEBUG, "%s Frame too early: Shift jitter buffer to the future, to make the frame fit to the end. (offset_timestamp(%d) > max_window_size(%d))\n", jb->name, offset_timestamp, jb->max_window_size);
/* shift window so it fits to the end of window */
jb->window_timestamp = timestamp - jb->max_window_size;
} else {
PDEBUG(DJITTER, DEBUG_DEBUG, "%s Frame too early: Shift jitter buffer to the future, to make the frame fit to the target delay. (offset_timestamp(%d) > max_window_size(%d))\n", jb->name, offset_timestamp, jb->max_window_size);
/* shift window so frame fits to the start of window + target delay */
jb->window_timestamp = timestamp - (uint32_t)(jb->target_window_size);
}
}
/* is frame is too late, shift window to the past. */
if (offset_timestamp < 0) {
if ((jb->window_flags & JITTER_FLAG_LATENCY)) {
PDEBUG(DJITTER, DEBUG_DEBUG, "%s Frame too late: Shift jitter buffer to the past, and add target window size. (offset_timestamp(%d) < 0)\n", jb->name, offset_timestamp);
/* shift window so frame fits to the start of window + half of target delay */
jb->window_timestamp = timestamp - (uint32_t)(jb->target_window_size) / 2;
} else {
PDEBUG(DJITTER, DEBUG_DEBUG, "%s Frame too late: Shift jitter buffer to the past, and add half target window size. (offset_timestamp(%d) < 0)\n", jb->name, offset_timestamp);
/* shift window so frame fits to the start of window + target delay */
jb->window_timestamp = timestamp - (uint32_t)(jb->target_window_size);
}
}
/* insert or append frame */
#ifdef HEAVY_DEBUG
PDEBUG(DJITTER, DEBUG_DEBUG, "%s Store frame\n", jb->name);
#endif
jf = malloc(sizeof(*jf) + length * jb->sample_size);
if (!jf) {
PDEBUG(DJITTER, DEBUG_ERROR, "No memory for frame.\n");
return;
}
memset(jf, 0, sizeof(*jf)); // note: clear header only
jf->sequence = sequence;
jf->timestamp = timestamp;
memcpy(jf->samples, samples, length * jb->sample_size);
jf->length = length;
jf->next = *jfp;
*jfp = jf;
}
/* get audio from jitterbuffer
*/
void jitter_load(jitter_t *jb, sample_t *samples, int length)
void jitter_load(jitter_t *jb, void *samples, int length)
{
sample_t *spl;
int inptr, outptr, len, fill;
int i, ii;
jitter_frame_t *jf;
int32_t count, count2, index;
spl = jb->spl;
inptr = jb->inptr;
outptr = jb->outptr;
len = jb->len;
fill = (inptr - outptr + len) % len;
#ifdef HEAVY_DEBUG
PDEBUG(DJITTER, DEBUG_DEBUG, "%sLoad chunk of %d samples.\n", jb->name, length);
#endif
if (fill < length)
ii = fill;
else
ii = length;
/* now unlock jitter buffer */
jb->unlocked = 1;
/* fill what we got */
for (i = 0; i < ii; i++) {
*samples++ = spl[outptr++];
if (outptr == len)
outptr = 0;
}
/* on underrun, fill with silence */
for (; i < length; i++) {
*samples++ = 0;
/* reduce delay */
jb->delay_counter += jb->sample_duration * (double)length;
if (jb->delay_counter >= jb->delay_interval) {
if (jb->min_delay_value >= 0)
PDEBUG(DJITTER, DEBUG_DEBUG, "%s Statistics: target_window_delay=%.0fms max_window_delay=%.0fms current min_delay=%.0fms\n", jb->name, (double)jb->target_window_size * jb->sample_duration * 1000.0, (double)jb->max_window_size * jb->sample_duration * 1000.0, (double)jb->min_delay_value * jb->sample_duration * 1000.0);
/* delay reduction, if maximum delay is greater than target jitter window size */
if ((jb->window_flags & JITTER_FLAG_LATENCY) && jb->min_delay_value > jb->target_window_size) {
PDEBUG(DJITTER, DEBUG_DEBUG, "%s Reducing current minimum delay of %.0fms, because maximum delay is greater than target window size of %.0fms.\n", jb->name, (double)jb->min_delay_value * jb->sample_duration * 1000.0, (double)jb->target_window_size * jb->sample_duration * 1000.0);
/* only reduce delay to half of the target window size */
jb->window_timestamp += jb->min_delay_value - jb->target_window_size / 2;
}
jb->delay_counter -= jb->delay_interval;
jb->delay_interval = REPEAT_DELAY_INTERVAL;
jb->min_delay_value = -1;
}
jb->outptr = outptr;
}
void jitter_clear(jitter_t *jb)
{
jb->inptr = jb->outptr = 0;
/* process all frames until output buffer is loaded */
while (length) {
/* always get frame with the lowest sequence number (1st frame) */
jf = jb->frame_list;
if (jf) {
count = jf->timestamp - jb->window_timestamp;
if (count > length)
count = length;
} else
count = length;
/* if there is no frame or we have not reached frame's time stamp, extrapolate */
if (count > 0) {
#ifdef HEAVY_DEBUG
if (jf)
PDEBUG(DJITTER, DEBUG_DEBUG, "%s There is a frame ahead in buffer after %d samples. Interpolating gap.\n", jb->name, jf->timestamp - jb->window_timestamp);
else
PDEBUG(DJITTER, DEBUG_DEBUG, "%s There is no frame ahead in buffer. Interpolating gap.\n", jb->name);
#endif
/* extrapolate by playing the extrapolation buffer */
while (count) {
count2 = count;
if (count2 > jb->extra_size - jb->extra_index)
count2 = jb->extra_size - jb->extra_index;
memcpy(samples, (uint8_t *)jb->extra_samples + jb->extra_index * jb->sample_size, count2 * jb->sample_size);
jb->extra_index += count2;
if (jb->extra_index == jb->extra_size) {
jb->extra_index = 0;
if ((jb->window_flags & JITTER_FLAG_REPEAT) && jb->extra_timeout_count < jb->extra_timeout_max) {
jb->extra_timeout_count++;
if (jb->extra_timeout_count == jb->extra_timeout_max) {
#ifdef HEAVY_DEBUG
PDEBUG(DJITTER, DEBUG_DEBUG, "%s Repeated jitter buffer enough, clearing to silence.\n", jb->name);
#endif
clear_extra_buffer(jb);
}
}
}
samples = (uint8_t *)samples + count2 * jb->sample_size;
length -= count2;
jb->window_timestamp += count2;
count -= count2;
}
if (length == 0)
return;
}
/* copy samples from frame (what is not in the past) */
index = jb->window_timestamp - jf->timestamp;
while (index < jf->length) {
/* use the lowest value of 'playout length' or 'remaining packet length' */
count = length;
if (jf->length - index < count)
count = jf->length - index;
/* if extrapolation is to be written, limit count to what we can store into buffer */
if ((jb->window_flags & JITTER_FLAG_REPEAT) && jb->extra_size - jb->extra_index < count)
count = jb->extra_size - jb->extra_index;
/* copy samples from packet to play out, increment sample pointer and decrement length */
#ifdef HEAVY_DEBUG
PDEBUG(DJITTER, DEBUG_DEBUG, "%s Copy data (offset=%u count=%u) from frame (sequence=%u timestamp=%u length=%u).\n", jb->name, index, count, jf->sequence, jf->timestamp, jf->length);
#endif
memcpy(samples, (uint8_t *)jf->samples + index * jb->sample_size, count * jb->sample_size);
samples = (uint8_t *)samples + count * jb->sample_size;
length -= count;
/* copy frame data to extrapolation buffer also, increment index */
if ((jb->window_flags & JITTER_FLAG_REPEAT)) {
memcpy((uint8_t *)jb->extra_samples + jb->extra_index * jb->sample_size, (uint8_t *)jf->samples + index * jb->sample_size, count * jb->sample_size);
jb->extra_index += count;
if (jb->extra_index == jb->extra_size)
jb->extra_index = 0;
jb->extra_timeout_count = 0; /* now we have new data, we reset timeout condition */
}
/* increment time stamp */
jb->window_timestamp += count;
index += count;
/* if there was enough to play out, we are done */
if (length == 0)
return;
}
/* free frame, because all samples are now in the past */
jb->frame_list = jf->next;
free(jf);
/* now go for next loop, in case there is still date to play out */
}
}

View File

@ -1,14 +1,60 @@
#define JITTER_FLAG_NONE 0 // no flags at all
#define JITTER_FLAG_LATENCY (1 << 0) // keep latency close to target_window_duration
#define JITTER_FLAG_REPEAT (1 << 1) // repeat audio to extrapolate gaps
/* window settings for low latency audio and extrapolation of gaps */
#define JITTER_AUDIO 0.050, 1.000, JITTER_FLAG_LATENCY | JITTER_FLAG_REPEAT
/* window settings for analog data (fax/modem) or digial data (HDLC) */
#define JITTER_DATA 0.100, 0.200, JITTER_FLAG_NONE
typedef struct jitter_frame {
struct jitter_frame *next;
uint16_t sequence;
uint32_t timestamp;
int length;
uint8_t samples[0];
} jitter_frame_t;
typedef struct jitter {
sample_t *spl; /* pointer to sample buffer */
int len; /* buffer size: number of samples */
int inptr, outptr; /* write pointer and read pointer */
char name[64];
/* sample properties */
int sample_size;
double sample_duration;
/* automatic sequence generation */
uint16_t next_sequence;
uint32_t next_timestamp;
/* window properties */
int unlocked;
uint32_t window_flags;
int target_window_size;
int max_window_size;
int window_valid;
uint32_t window_ssrc;
uint32_t window_timestamp;
/* reduction of delay */
double delay_interval;
double delay_counter;
int32_t min_delay_value;
/* extrapolation */
int extra_size;
int extra_index;
void *extra_samples;
int extra_timeout_max;
int extra_timeout_count;
/* list of frames */
jitter_frame_t *frame_list;
} jitter_t;
int jitter_create(jitter_t *jitter, int length);
void jitter_reset(jitter_t *jitter);
void jitter_destroy(jitter_t *jitter);
void jitter_save(jitter_t *jb, sample_t *samples, int length);
void jitter_load(jitter_t *jb, sample_t *samples, int length);
void jitter_clear(jitter_t *jb);
int jitter_create(jitter_t *jb, const char *name, double samplerate, int sample_size, double target_window_duration, double max_window_duration, uint32_t window_flags);
void jitter_reset(jitter_t *jb);
void jitter_destroy(jitter_t *jb);
void jitter_save(jitter_t *jb, void *samples, int length, int has_sequence, uint16_t sequence, uint32_t timestamp, uint32_t ssrc);
void jitter_load(jitter_t *jb, void *samples, int length);

View File

@ -61,7 +61,7 @@ static int release_on_disconnect; /* release towards mobile phone, if OSMO-CC ca
osmo_cc_endpoint_t endpoint, *ep;
void encode_l16(uint8_t *src_data, int src_len, uint8_t **dst_data, int *dst_len)
void encode_l16(uint8_t *src_data, int src_len, uint8_t **dst_data, int *dst_len, void __attribute__((unused)) *arg)
{
uint16_t *src = (uint16_t *)src_data, *dst;
int len = src_len / 2, i;
@ -75,7 +75,7 @@ void encode_l16(uint8_t *src_data, int src_len, uint8_t **dst_data, int *dst_len
*dst_len = len * 2;
}
void decode_l16(uint8_t *src_data, int src_len, uint8_t **dst_data, int *dst_len)
void decode_l16(uint8_t *src_data, int src_len, uint8_t **dst_data, int *dst_len, void __attribute__((unused)) *arg)
{
uint16_t *src = (uint16_t *)src_data, *dst;
int len = src_len / 2, i;
@ -387,7 +387,7 @@ static void process_timeout(struct timer *timer)
}
}
void down_audio(struct osmo_cc_session_codec *codec, uint16_t __attribute__((unused)) sequence_number, uint32_t __attribute__((unused)) timestamp, uint8_t *data, int len)
void down_audio(struct osmo_cc_session_codec *codec, uint8_t __attribute__((unused)) marker, uint16_t sequence_number, uint32_t timestamp, uint32_t ssrc, uint8_t *data, int len)
{
process_t *process = codec->media->session->priv;
sample_t samples[len / 2];
@ -395,12 +395,12 @@ void down_audio(struct osmo_cc_session_codec *codec, uint16_t __attribute__((unu
/* if we are disconnected, ignore audio */
if (!process || process->pattern != PATTERN_NONE)
return;
int16_to_samples(samples, (int16_t *)data, len / 2);
int16_to_samples_speech(samples, (int16_t *)data, len / 2);
#ifdef DEBUG_LEVEL
double lev = level_of(samples, len / 2);
printf("festnetz-level: %s %.4f\n", debug_db(lev), (20 * log10(lev)));
#endif
call_down_audio(process->callref, samples, len / 2);
call_down_audio(process->callref, sequence_number, timestamp, ssrc, samples, len / 2);
}
static void indicate_setup(process_t *process, const char *callerid, const char *dialing, uint8_t network_type, const char *network_id)
@ -621,8 +621,8 @@ void call_up_audio(int callref, sample_t *samples, int count)
double lev = level_of(samples, count);
printf(" mobil-level: %s%.4f\n", debug_db(lev), (20 * log10(lev)));
#endif
samples_to_int16(data, samples, count);
osmo_cc_rtp_send(process->codec, (uint8_t *)data, count * 2, 1, count);
samples_to_int16_speech(data, samples, count);
osmo_cc_rtp_send(process->codec, (uint8_t *)data, count * 2, 0, 1, count, process);
/* don't destroy process here in case of an error */
}
@ -645,7 +645,7 @@ void call_clock(void)
printf(" mobil-level: %s%.4f\n", debug_db(lev), (20 * log10(lev)));
samples_to_int16(data, samples, 160);
#endif
osmo_cc_rtp_send(process->codec, (uint8_t *)data, 160 * 2, 1, 160);
osmo_cc_rtp_send(process->codec, (uint8_t *)data, 160 * 2, 0, 1, 160, process);
/* don't destroy process here in case of an error */
}
process = process->next;
@ -935,7 +935,7 @@ void call_media_handle(void)
while(process) {
if (process->session)
osmo_cc_session_handle(process->session);
osmo_cc_session_handle(process->session, process);
process = process->next;
}
}

View File

@ -37,7 +37,7 @@ void call_down_release(int callref, int cause);
/* send and receive audio */
void call_up_audio(int callref, sample_t *samples, int count);
void call_down_audio(int callref, sample_t *samples, int count);
void call_down_audio(int callref, uint16_t sequence, uint32_t timestamp, uint32_t ssrc, sample_t *samples, int count);
/* clock to transmit to */
void call_clock(void); /* from main loop */

View File

@ -89,8 +89,8 @@ static console_t console;
extern osmo_cc_endpoint_t *ep;
void encode_l16(uint8_t *src_data, int src_len, uint8_t **dst_data, int *dst_len);
void decode_l16(uint8_t *src_data, int src_len, uint8_t **dst_data, int *dst_len);
void encode_l16(uint8_t *src_data, int src_len, uint8_t **dst_data, int *dst_len, void __attribute__((unused)) *arg);
void decode_l16(uint8_t *src_data, int src_len, uint8_t **dst_data, int *dst_len, void __attribute__((unused)) *arg);
static struct osmo_cc_helper_audio_codecs codecs[] = {
{ "L16", 8000, 1, encode_l16, decode_l16 },
@ -141,28 +141,26 @@ static void free_console(void)
console.callref = 0;
}
void up_audio(struct osmo_cc_session_codec *codec, uint16_t __attribute__((unused)) sequence_number, uint32_t __attribute__((unused)) timestamp, uint8_t *data, int len)
void up_audio(struct osmo_cc_session_codec *codec, uint8_t __attribute__((unused)) marker, uint16_t sequence_number, uint32_t timestamp, uint32_t ssrc, uint8_t *data, int len)
{
int count = len / 2;
sample_t samples[count];
/* save audio from transceiver to jitter buffer */
if (console.sound) {
sample_t up[(int)((double)count * console.srstate.factor + 0.5) + 10];
int16_to_samples(samples, (int16_t *)data, count);
count = samplerate_upsample(&console.srstate, samples, count, up);
jitter_save(&console.dejitter, up, count);
int16_to_samples_speech(samples, (int16_t *)data, count);
jitter_save(&console.dejitter, samples, count, 1, sequence_number, timestamp, ssrc);
return;
}
/* if echo test is used, send echo back to mobile */
if (console.echo_test) {
osmo_cc_rtp_send(codec, (uint8_t *)data, count * 2, 1, count);
osmo_cc_rtp_send(codec, (uint8_t *)data, count * 2, 0, 1, count, &console);
return;
}
/* if no sound is used, send test tone to mobile */
if (console.state == CONSOLE_CONNECT) {
get_test_patterns((int16_t *)data, count);
osmo_cc_rtp_send(codec, (uint8_t *)data, count * 2, 1, count);
osmo_cc_rtp_send(codec, (uint8_t *)data, count * 2, 0, 1, count, &console);
return;
}
}
@ -398,7 +396,7 @@ int console_init(const char *audiodev, int samplerate, int buffer, int loopback,
goto error;
}
rc = jitter_create(&console.dejitter, samplerate / 5);
rc = jitter_create(&console.dejitter, "console", 8000, sizeof(sample_t), 0.050, 0.200, JITTER_FLAG_NONE);
if (rc < 0) {
PDEBUG(DSENDER, DEBUG_ERROR, "Failed to create and init dejitter buffer!\n");
goto error;
@ -571,7 +569,7 @@ void process_console(int c)
process_ui(c);
if (console.session)
osmo_cc_session_handle(console.session);
osmo_cc_session_handle(console.session, &console);
if (!console.sound)
return;
@ -580,7 +578,7 @@ void process_console(int c)
/* handle audio, if sound device is used */
sample_t samples[console.buffer_size + 10], *samples_list[1];
uint8_t *power_list[1];
int count;
int count, input_num;
int rc;
count = sound_get_tosend(console.sound, console.buffer_size);
@ -591,7 +589,11 @@ void process_console(int c)
return;
}
if (count > 0) {
jitter_load(&console.dejitter, samples, count);
/* load and upsample */
input_num = samplerate_upsample_input_num(&console.srstate, count);
jitter_load(&console.dejitter, samples, input_num);
samplerate_upsample(&console.srstate, samples, input_num, samples, count);
/* write to sound device */
samples_list[0] = samples;
power_list[0] = NULL;
rc = sound_write(console.sound, samples_list, power_list, count, NULL, NULL, 1);
@ -613,9 +615,9 @@ void process_console(int c)
if (count) {
int i;
if (console.loopback == 3)
jitter_save(&console.dejitter, samples, count);
count = samplerate_downsample(&console.srstate, samples, count);
if (console.loopback == 3)
jitter_save(&console.dejitter, samples, count, 0, 0, 0, 0);
/* put samples into ring buffer */
for (i = 0; i < count; i++) {
console.tx_buffer[console.tx_buffer_pos] = samples[i];
@ -625,8 +627,8 @@ void process_console(int c)
/* only if we have a call */
if (console.callref && console.codec) {
int16_t data[160];
samples_to_int16(data, console.tx_buffer, 160);
osmo_cc_rtp_send(console.codec, (uint8_t *)data, 160 * 2, 1, 160);
samples_to_int16_speech(data, console.tx_buffer, 160);
osmo_cc_rtp_send(console.codec, (uint8_t *)data, 160 * 2, 0, 1, 160, &console);
}
}
}

View File

@ -149,7 +149,13 @@ int sender_create(sender_t *sender, const char *kanal, double sendefrequenz, dou
goto error;
}
rc = jitter_create(&sender->dejitter, samplerate / 5);
rc = jitter_create(&sender->dejitter, sender->kanal, 8000, sizeof(sample_t), JITTER_AUDIO);
if (rc < 0) {
PDEBUG(DSENDER, DEBUG_ERROR, "Failed to create and init audio buffer!\n");
goto error;
}
rc = jitter_create(&sender->loop_dejitter, sender->kanal, samplerate, sizeof(sample_t), JITTER_AUDIO);
if (rc < 0) {
PDEBUG(DSENDER, DEBUG_ERROR, "Failed to create and init audio buffer!\n");
goto error;
@ -288,6 +294,7 @@ void sender_destroy(sender_t *sender)
wave_destroy_playback(&sender->wave_tx_play);
jitter_destroy(&sender->dejitter);
jitter_destroy(&sender->loop_dejitter);
}
/* set frequency modulation and parameters */
@ -373,7 +380,7 @@ cant_recover:
for (i = 0, inst = sender; inst; i++, inst = inst->slave) {
/* load TX data from audio loop or from sender instance */
if (inst->loopback == 3)
jitter_load(&inst->dejitter, samples[i], count);
jitter_load(&inst->loop_dejitter, samples[i], count);
else
sender_send(inst, samples[i], power[i], count);
/* internal loopback: loop back TX audio to RX */
@ -458,7 +465,7 @@ cant_recover:
sender_receive(inst, samples[i], count, rf_level_db[i]);
}
if (inst->loopback == 3)
jitter_save(&inst->dejitter, samples[i], count);
jitter_save(&inst->loop_dejitter, samples[i], count, 0, 0, 0, 0);
}
}
#ifdef DEBUG_TIME_CONSUMPTION

View File

@ -73,6 +73,7 @@ typedef struct sender {
/* audio buffer for audio to send to transmitter (also used as loopback buffer) */
jitter_t dejitter;
jitter_t loop_dejitter;
/* audio buffer for audio to send to caller (20ms = 160 samples @ 8000Hz) */
sample_t rxbuf[160];

View File

@ -568,6 +568,30 @@ static void notify_ind(osmo_cc_call_t *call, osmo_cc_msg_t *msg)
forward_to_ul(call, msg);
}
static void modify_req(osmo_cc_call_t *call, osmo_cc_msg_t *msg)
{
/* to lower layer */
forward_to_ll(call, msg);
}
static void modify_cnf(osmo_cc_call_t *call, osmo_cc_msg_t *msg)
{
/* to upper layer */
forward_to_ul(call, msg);
}
static void modify_ind(osmo_cc_call_t *call, osmo_cc_msg_t *msg)
{
/* to upper layer */
forward_to_ul(call, msg);
}
static void modify_rsp(osmo_cc_call_t *call, osmo_cc_msg_t *msg)
{
/* to lower layer */
forward_to_ll(call, msg);
}
static void disc_req(osmo_cc_call_t *call, osmo_cc_msg_t *msg)
{
/* change state */
@ -853,6 +877,14 @@ static struct statemachine {
OSMO_CC_MSG_INFO_IND, info_ind},
{SBIT(OSMO_CC_STATE_ACTIVE),
OSMO_CC_MSG_INFO_REQ, info_req},
{SBIT(OSMO_CC_STATE_ACTIVE),
OSMO_CC_MSG_MODIFY_REQ, modify_req},
{SBIT(OSMO_CC_STATE_ACTIVE),
OSMO_CC_MSG_MODIFY_CNF, modify_cnf},
{SBIT(OSMO_CC_STATE_ACTIVE),
OSMO_CC_MSG_MODIFY_IND, modify_ind},
{SBIT(OSMO_CC_STATE_ACTIVE),
OSMO_CC_MSG_MODIFY_RSP, modify_rsp},
/* call release */
{SBIT(OSMO_CC_STATE_INIT_OUT) | SBIT(OSMO_CC_STATE_INIT_IN) |

View File

@ -29,7 +29,7 @@
#include "endpoint.h"
#include "helper.h"
osmo_cc_session_t *osmo_cc_helper_audio_offer(osmo_cc_session_config_t *conf, void *priv, struct osmo_cc_helper_audio_codecs *codecs, void (*receiver)(struct osmo_cc_session_codec *codec, uint16_t sequence_number, uint32_t timestamp, uint8_t *data, int len), osmo_cc_msg_t *msg, int debug)
osmo_cc_session_t *osmo_cc_helper_audio_offer(osmo_cc_session_config_t *conf, void *priv, struct osmo_cc_helper_audio_codecs *codecs, void (*receiver)(struct osmo_cc_session_codec *codec, uint8_t marker, uint16_t sequence_number, uint32_t timestamp, uint32_t ssrc, uint8_t *data, int len), osmo_cc_msg_t *msg, int debug)
{
osmo_cc_session_t *session;
osmo_cc_session_media_t *media;
@ -52,7 +52,7 @@ osmo_cc_session_t *osmo_cc_helper_audio_offer(osmo_cc_session_config_t *conf, vo
return session;
}
const char *osmo_cc_helper_audio_accept(osmo_cc_session_config_t *conf, void *priv, struct osmo_cc_helper_audio_codecs *codecs, void (*receiver)(struct osmo_cc_session_codec *codec, uint16_t sequence_number, uint32_t timestamp, uint8_t *data, int len), osmo_cc_msg_t *msg, osmo_cc_session_t **session_p, osmo_cc_session_codec_t **codec_p, int force_our_codec)
const char *osmo_cc_helper_audio_accept(osmo_cc_session_config_t *conf, void *priv, struct osmo_cc_helper_audio_codecs *codecs, void (*receiver)(struct osmo_cc_session_codec *codec, uint8_t marker, uint16_t sequence_number, uint32_t timestamp, uint32_t ssrc, uint8_t *data, int len), osmo_cc_msg_t *msg, osmo_cc_session_t **session_p, osmo_cc_session_codec_t **codec_p, int force_our_codec)
{
char offer_sdp[65536];
const char *accept_sdp;
@ -125,6 +125,7 @@ const char *osmo_cc_helper_audio_accept(osmo_cc_session_config_t *conf, void *pr
if (!selected_codec) {
PDEBUG(DCC, DEBUG_ERROR, "No codec found in setup message that we support.\n");
osmo_cc_free_session(*session_p);
*session_p = NULL;
return NULL;
}
osmo_cc_session_accept_codec(selected_codec, codecs[selected_codec_i].encoder, codecs[selected_codec_i].decoder);
@ -138,6 +139,7 @@ const char *osmo_cc_helper_audio_accept(osmo_cc_session_config_t *conf, void *pr
accept_sdp = osmo_cc_session_send_answer(*session_p);
if (!accept_sdp) {
osmo_cc_free_session(*session_p);
*session_p = NULL;
return NULL;
}

View File

@ -3,11 +3,11 @@ struct osmo_cc_helper_audio_codecs {
const char *payload_name;
uint32_t payload_rate;
int payload_channels;
void (*encoder)(uint8_t *src_data, int src_len, uint8_t **dst_data, int *dst_len);
void (*decoder)(uint8_t *src_data, int src_len, uint8_t **dst_data, int *dst_len);
void (*encoder)(uint8_t *src_data, int src_len, uint8_t **dst_data, int *dst_len, void *priv);
void (*decoder)(uint8_t *src_data, int src_len, uint8_t **dst_data, int *dst_len, void *priv);
};
osmo_cc_session_t *osmo_cc_helper_audio_offer(osmo_cc_session_config_t *conf, void *priv, struct osmo_cc_helper_audio_codecs *codecs, void (*receiver)(struct osmo_cc_session_codec *codec, uint16_t sequence_number, uint32_t timestamp, uint8_t *data, int len), osmo_cc_msg_t *msg, int debug);
const char *osmo_cc_helper_audio_accept(osmo_cc_session_config_t *conf, void *priv, struct osmo_cc_helper_audio_codecs *codecs, void (*receiver)(struct osmo_cc_session_codec *codec, uint16_t sequence_number, uint32_t timestamp, uint8_t *data, int len), osmo_cc_msg_t *msg, osmo_cc_session_t **session_p, osmo_cc_session_codec_t **codec_p, int force_our_codec);
osmo_cc_session_t *osmo_cc_helper_audio_offer(osmo_cc_session_config_t *conf, void *priv, struct osmo_cc_helper_audio_codecs *codecs, void (*receiver)(struct osmo_cc_session_codec *codec, uint8_t marker, uint16_t sequence_number, uint32_t timestamp, uint32_t ssrc, uint8_t *data, int len), osmo_cc_msg_t *msg, int debug);
const char *osmo_cc_helper_audio_accept(osmo_cc_session_config_t *conf, void *priv, struct osmo_cc_helper_audio_codecs *codecs, void (*receiver)(struct osmo_cc_session_codec *codec, uint8_t marker, uint16_t sequence_number, uint32_t timestamp, uint32_t ssrc, uint8_t *data, int len), osmo_cc_msg_t *msg, osmo_cc_session_t **session_p, osmo_cc_session_codec_t **codec_p, int force_our_codec);
int osmo_cc_helper_audio_negotiate(osmo_cc_msg_t *msg, osmo_cc_session_t **session_p, osmo_cc_session_codec_t **codec_p);

View File

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

View File

@ -30,6 +30,10 @@ enum osmo_cc_msg_type {
OSMO_CC_MSG_NOTIFY_IND = 0x85,
OSMO_CC_MSG_INFO_REQ = 0x88,
OSMO_CC_MSG_INFO_IND = 0x89,
OSMO_CC_MSG_MODIFY_REQ = 0x90,
OSMO_CC_MSG_MODIFY_IND = 0x91,
OSMO_CC_MSG_MODIFY_RSP = 0x92,
OSMO_CC_MSG_MODIFY_CNF = 0x93,
OSMO_CC_MSG_ATTACH_REQ = 0xf8,
OSMO_CC_MSG_ATTACH_IND = 0xf9,
OSMO_CC_MSG_ATTACH_RSP = 0xfa,

View File

@ -52,7 +52,7 @@ struct rtp_x_hdr {
uint16_t length;
} __attribute__((packed));
static int rtp_receive(int sock, uint8_t **payload_p, int *payload_len_p, uint8_t *marker_p, uint8_t *pt_p, uint16_t *sequence_p, uint32_t *timestamp_p)
static int rtp_receive(int sock, uint8_t **payload_p, int *payload_len_p, uint8_t *marker_p, uint8_t *pt_p, uint16_t *sequence_p, uint32_t *timestamp_p, uint32_t *ssrc_p)
{
static uint8_t data[2048];
int len;
@ -83,6 +83,7 @@ static int rtp_receive(int sock, uint8_t **payload_p, int *payload_len_p, uint8_
payload_type = rtph->byte1 & 0x7f;
*sequence_p = ntohs(rtph->sequence);
*timestamp_p = ntohl(rtph->timestamp);
*ssrc_p = ntohl(rtph->ssrc);
if (version != RTP_VERSION) {
PDEBUG(DCC, DEBUG_NOTICE, "Received RTP version %d not supported.\n", version);
@ -131,7 +132,7 @@ static int rtp_receive(int sock, uint8_t **payload_p, int *payload_len_p, uint8_
return 0;
}
static void rtp_send(int sock, uint8_t *payload, int payload_len, uint8_t pt, uint16_t sequence, uint32_t timestamp, uint32_t ssrc)
static void rtp_send(int sock, uint8_t *payload, int payload_len, uint8_t marker, uint8_t pt, uint16_t sequence, uint32_t timestamp, uint32_t ssrc)
{
struct rtp_hdr *rtph;
char data[sizeof(*rtph) + payload_len];
@ -140,7 +141,7 @@ static void rtp_send(int sock, uint8_t *payload, int payload_len, uint8_t pt, ui
rtph = (struct rtp_hdr *)data;
len = sizeof(*rtph);
rtph->byte0 = RTP_VERSION << 6;
rtph->byte1 = pt;
rtph->byte1 = pt | (marker << 7);
rtph->sequence = htons(sequence);
rtph->timestamp = htonl(timestamp);
rtph->ssrc = htonl(ssrc);
@ -172,7 +173,7 @@ int osmo_cc_rtp_open(osmo_cc_session_media_t *media)
int flags;
int rc;
media->rtp_ssrc = rand();
media->tx_ssrc = rand();
osmo_cc_rtp_close(media);
@ -320,7 +321,7 @@ connect_error:
}
/* send rtp data with given codec */
void osmo_cc_rtp_send(osmo_cc_session_codec_t *codec, uint8_t *data, int len, int inc_sequence, int inc_timestamp)
void osmo_cc_rtp_send(osmo_cc_session_codec_t *codec, uint8_t *data, int len, uint8_t marker, int inc_sequence, int inc_timestamp, void *priv)
{
uint8_t *payload = NULL;
int payload_len = 0;
@ -329,13 +330,13 @@ void osmo_cc_rtp_send(osmo_cc_session_codec_t *codec, uint8_t *data, int len, in
return;
if (codec->encoder)
codec->encoder(data, len, &payload, &payload_len);
codec->encoder(data, len, &payload, &payload_len, priv);
else {
payload = data;
payload_len = len;
}
rtp_send(codec->media->rtp_socket, payload, payload_len, codec->payload_type_remote, codec->media->tx_sequence, codec->media->tx_timestamp, codec->media->rtp_ssrc);
rtp_send(codec->media->rtp_socket, payload, payload_len, marker, codec->payload_type_remote, codec->media->tx_sequence, codec->media->tx_timestamp, codec->media->tx_ssrc);
codec->media->tx_sequence += inc_sequence;
codec->media->tx_timestamp += inc_timestamp;
@ -344,7 +345,7 @@ void osmo_cc_rtp_send(osmo_cc_session_codec_t *codec, uint8_t *data, int len, in
}
/* receive rtp data for given media, return < 0, if there is nothing this time */
int osmo_cc_rtp_receive(osmo_cc_session_media_t *media)
int osmo_cc_rtp_receive(osmo_cc_session_media_t *media, void *priv)
{
int rc;
uint8_t *payload = NULL;
@ -358,7 +359,7 @@ int osmo_cc_rtp_receive(osmo_cc_session_media_t *media)
if (!media || media->rtp_socket <= 0)
return -EIO;
rc = rtp_receive(media->rtp_socket, &payload, &payload_len, &marker, &payload_type, &media->rx_sequence, &media->rx_timestamp);
rc = rtp_receive(media->rtp_socket, &payload, &payload_len, &marker, &payload_type, &media->rx_sequence, &media->rx_timestamp, &media->rx_ssrc);
if (rc < 0)
return rc;
@ -374,14 +375,14 @@ int osmo_cc_rtp_receive(osmo_cc_session_media_t *media)
}
if (codec->decoder)
codec->decoder(payload, payload_len, &data, &len);
codec->decoder(payload, payload_len, &data, &len, priv);
else {
data = payload;
len = payload_len;
}
if (codec->media->receive)
codec->media->receiver(codec, media->rx_sequence, media->rx_timestamp, data, len);
codec->media->receiver(codec, marker, media->rx_sequence, media->rx_timestamp, media->rx_ssrc, data, len);
if (codec->decoder)
free(data);

View File

@ -2,7 +2,7 @@
void osmo_cc_set_rtp_ports(osmo_cc_session_config_t *conf, uint16_t from, uint16_t to);
int osmo_cc_rtp_open(osmo_cc_session_media_t *media);
int osmo_cc_rtp_connect(osmo_cc_session_media_t *media);
void osmo_cc_rtp_send(osmo_cc_session_codec_t *codec, uint8_t *data, int len, int inc_sequence, int inc_timestamp);
int osmo_cc_rtp_receive(osmo_cc_session_media_t *media);
void osmo_cc_rtp_send(osmo_cc_session_codec_t *codec, uint8_t *data, int len, uint8_t marker, int inc_sequence, int inc_timestamp, void *priv);
int osmo_cc_rtp_receive(osmo_cc_session_media_t *media, void *priv);
void osmo_cc_rtp_close(osmo_cc_session_media_t *media);

View File

@ -127,7 +127,7 @@ void osmo_cc_free_session(osmo_cc_session_t *session)
free(session);
}
osmo_cc_session_media_t *osmo_cc_add_media(osmo_cc_session_t *session, enum osmo_cc_session_nettype nettype, enum osmo_cc_session_addrtype addrtype, const char *address, enum osmo_cc_session_media_type type, uint16_t port, enum osmo_cc_session_media_proto proto, int send, int receive, void (*receiver)(struct osmo_cc_session_codec *codec, uint16_t sequence_number, uint32_t timestamp, uint8_t *data, int len), int debug)
osmo_cc_session_media_t *osmo_cc_add_media(osmo_cc_session_t *session, enum osmo_cc_session_nettype nettype, enum osmo_cc_session_addrtype addrtype, const char *address, enum osmo_cc_session_media_type type, uint16_t port, enum osmo_cc_session_media_proto proto, int send, int receive, void (*receiver)(struct osmo_cc_session_codec *codec, uint8_t marker, uint16_t sequence_number, uint32_t timestamp, uint32_t ssrc, uint8_t *data, int len), int debug)
{
osmo_cc_session_config_t *conf = session->config;
osmo_cc_session_media_t *media, **mediap;
@ -197,7 +197,7 @@ void osmo_cc_free_media(osmo_cc_session_media_t *media)
free(media);
}
osmo_cc_session_codec_t *osmo_cc_add_codec(osmo_cc_session_media_t *media, const char *payload_name, uint32_t payload_rate, int payload_channels, void (*encoder)(uint8_t *src_data, int src_len, uint8_t **dst_data, int *dst_len), void (*decoder)(uint8_t *src_data, int src_len, uint8_t **dst_data, int *dst_len), int debug)
osmo_cc_session_codec_t *osmo_cc_add_codec(osmo_cc_session_media_t *media, const char *payload_name, uint32_t payload_rate, int payload_channels, void (*encoder)(uint8_t *src_data, int src_len, uint8_t **dst_data, int *dst_len, void *priv), void (*decoder)(uint8_t *src_data, int src_len, uint8_t **dst_data, int *dst_len, void *priv), int debug)
{
osmo_cc_session_codec_t *codec, **codecp;
int rc;
@ -374,7 +374,7 @@ osmo_cc_session_t *osmo_cc_session_receive_offer(osmo_cc_session_config_t *conf,
return session;
}
void osmo_cc_session_accept_media(osmo_cc_session_media_t *media, enum osmo_cc_session_nettype nettype, enum osmo_cc_session_addrtype addrtype, const char *address, int send, int receive, void (*receiver)(struct osmo_cc_session_codec *codec, uint16_t sequence_number, uint32_t timestamp, uint8_t *data, int len))
void osmo_cc_session_accept_media(osmo_cc_session_media_t *media, enum osmo_cc_session_nettype nettype, enum osmo_cc_session_addrtype addrtype, const char *address, int send, int receive, void (*receiver)(struct osmo_cc_session_codec *codec, uint8_t marker, uint16_t sequence_number, uint32_t timestamp, uint32_t ssrc, uint8_t *data, int len))
{
osmo_cc_session_config_t *conf = media->session->config;
@ -403,7 +403,7 @@ void osmo_cc_session_accept_media(osmo_cc_session_media_t *media, enum osmo_cc_s
}
void osmo_cc_session_accept_codec(osmo_cc_session_codec_t *codec, void (*encoder)(uint8_t *src_data, int src_len, uint8_t **dst_data, int *dst_len), void (*decoder)(uint8_t *src_data, int src_len, uint8_t **dst_data, int *dst_len))
void osmo_cc_session_accept_codec(osmo_cc_session_codec_t *codec, void (*encoder)(uint8_t *src_data, int src_len, uint8_t **dst_data, int *dst_len, void *priv), void (*decoder)(uint8_t *src_data, int src_len, uint8_t **dst_data, int *dst_len, void *priv))
{
codec->accepted = 1;
codec->encoder = encoder;
@ -622,14 +622,14 @@ int osmo_cc_session_if_codec(osmo_cc_session_codec_t *codec, const char *name, u
&& codec->payload_channels == channels);
}
int osmo_cc_session_handle(osmo_cc_session_t *session)
int osmo_cc_session_handle(osmo_cc_session_t *session, void *codec_priv)
{
osmo_cc_session_media_t *media;
int w = 0, rc;
osmo_cc_session_for_each_media(session->media_list, media) {
do {
rc = osmo_cc_rtp_receive(media);
rc = osmo_cc_rtp_receive(media, codec_priv);
if (rc >= 0)
w = 1;
} while (rc >= 0);

View File

@ -79,10 +79,10 @@ typedef struct osmo_cc_session_media {
osmo_cc_session_connection_data_t connection_data_local, connection_data_remote;
struct osmo_cc_session_codec *codec_list;
int send, receive;
void (*receiver)(struct osmo_cc_session_codec *codec, uint16_t sequence_number, uint32_t timestamp, uint8_t *data, int len);
void (*receiver)(struct osmo_cc_session_codec *codec, uint8_t marker, uint16_t sequence_number, uint32_t timestamp, uint32_t ssrc, uint8_t *data, int len);
int rtp_socket;
int rtcp_socket;
uint32_t rtp_ssrc;
uint32_t tx_ssrc, rx_ssrc;
uint16_t tx_sequence, rx_sequence;
uint32_t tx_timestamp, rx_timestamp;
int accepted;
@ -96,8 +96,8 @@ typedef struct osmo_cc_session_codec {
const char *payload_name;
uint32_t payload_rate;
int payload_channels;
void (*encoder)(uint8_t *src_data, int src_len, uint8_t **dst_data, int *dst_len);
void (*decoder)(uint8_t *src_data, int src_len, uint8_t **dst_data, int *dst_len);
void (*encoder)(uint8_t *src_data, int src_len, uint8_t **dst_data, int *dst_len, void *priv);
void (*decoder)(uint8_t *src_data, int src_len, uint8_t **dst_data, int *dst_len, void *priv);
int accepted;
} osmo_cc_session_codec_t;
@ -110,15 +110,15 @@ typedef struct osmo_cc_session_codec {
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);
osmo_cc_session_t *osmo_cc_new_session(osmo_cc_session_config_t *conf, void *priv, const char *username, const char *sess_id, const char *sess_version, enum osmo_cc_session_nettype nettype, enum osmo_cc_session_addrtype addrtype, const char *unicast_address, const char *session_name, int debug);
void osmo_cc_free_session(osmo_cc_session_t *session);
osmo_cc_session_media_t *osmo_cc_add_media(osmo_cc_session_t *session, enum osmo_cc_session_nettype nettype, enum osmo_cc_session_addrtype addrtype, const char *address, enum osmo_cc_session_media_type type, uint16_t port, enum osmo_cc_session_media_proto proto, int send, int receive, void (*receiver)(struct osmo_cc_session_codec *codec, uint16_t sequence_number, uint32_t timestamp, uint8_t *data, int len), int debug);
osmo_cc_session_media_t *osmo_cc_add_media(osmo_cc_session_t *session, enum osmo_cc_session_nettype nettype, enum osmo_cc_session_addrtype addrtype, const char *address, enum osmo_cc_session_media_type type, uint16_t port, enum osmo_cc_session_media_proto proto, int send, int receive, void (*receiver)(struct osmo_cc_session_codec *codec, uint8_t marker, uint16_t sequence_number, uint32_t timestamp, uint32_t ssrc, uint8_t *data, int len), int debug);
void osmo_cc_free_media(osmo_cc_session_media_t *media);
osmo_cc_session_codec_t *osmo_cc_add_codec(osmo_cc_session_media_t *media, const char *playload_name, uint32_t playload_rate, int playload_channels, void (*encoder)(uint8_t *src_data, int src_len, uint8_t **dst_data, int *dst_len), void (*decoder)(uint8_t *src_data, int src_len, uint8_t **dst_data, int *dst_len), int debug);
osmo_cc_session_codec_t *osmo_cc_add_codec(osmo_cc_session_media_t *media, const char *playload_name, uint32_t playload_rate, int playload_channels, void (*encoder)(uint8_t *src_data, int src_len, uint8_t **dst_data, int *dst_len, void *priv), void (*decoder)(uint8_t *src_data, int src_len, uint8_t **dst_data, int *dst_len, void *priv), int debug);
void osmo_cc_free_codec(osmo_cc_session_codec_t *codec);
int osmo_cc_session_check(struct osmo_cc_session *session, int remote);
const char *osmo_cc_session_send_offer(osmo_cc_session_t *session);
osmo_cc_session_t *osmo_cc_session_receive_offer(osmo_cc_session_config_t *conf, void *priv, const char *sdp);
void osmo_cc_session_accept_media(osmo_cc_session_media_t *media, enum osmo_cc_session_nettype nettype, enum osmo_cc_session_addrtype addrtype, const char *address, int send, int receive, void (*receiver)(struct osmo_cc_session_codec *codec, uint16_t sequence_number, uint32_t timestamp, uint8_t *data, int len));
void osmo_cc_session_accept_codec(osmo_cc_session_codec_t *codec, void (*encoder)(uint8_t *src_data, int src_len, uint8_t **dst_data, int *dst_len), void (*decoder)(uint8_t *src_data, int src_len, uint8_t **dst_data, int *dst_len));
void osmo_cc_session_accept_media(osmo_cc_session_media_t *media, enum osmo_cc_session_nettype nettype, enum osmo_cc_session_addrtype addrtype, const char *address, int send, int receive, void (*receiver)(struct osmo_cc_session_codec *codec, uint8_t marker, uint16_t sequence_number, uint32_t timestamp, uint32_t ssrc, uint8_t *data, int len));
void osmo_cc_session_accept_codec(osmo_cc_session_codec_t *codec, void (*encoder)(uint8_t *src_data, int src_len, uint8_t **dst_data, int *dst_len, void *priv), void (*decoder)(uint8_t *src_data, int src_len, uint8_t **dst_data, int *dst_len, void *priv));
const char *osmo_cc_session_send_answer(osmo_cc_session_t *session);
int osmo_cc_session_receive_answer(osmo_cc_session_t *session, const char *sdp);
const char *osmo_cc_session_nettype2string(enum osmo_cc_session_nettype nettype);
@ -126,5 +126,5 @@ const char *osmo_cc_session_addrtype2string(enum osmo_cc_session_addrtype addrty
const char *osmo_cc_session_media_type2string(enum osmo_cc_session_media_type media_type);
const char *osmo_cc_session_media_proto2string(enum osmo_cc_session_media_proto media_proto);
int osmo_cc_session_if_codec(osmo_cc_session_codec_t *codec, const char *name, uint32_t rate, int channels);
int osmo_cc_session_handle(osmo_cc_session_t *session);
int osmo_cc_session_handle(osmo_cc_session_t *session, void *codec_priv);

View File

@ -25,6 +25,7 @@
* of 16 bits signed value:
*/
static double int_16_speech_level = SPEECH_LEVEL * 0.7079; /* 16 dBm below dBm0, which is about 3dBm below full 16 bit range */
static double int_16_1mw_level = 0.7079; /* dBm0, 3dBm below full 16 bit range */
/* A sample_t is a value that has virtually infinite precision but will also
* support high numbers. 'double' or 'float' types are sufficient.
@ -40,7 +41,8 @@ static double int_16_speech_level = SPEECH_LEVEL * 0.7079; /* 16 dBm below dBm0,
* envelope is network dependent.
*/
void samples_to_int16(int16_t *spl, sample_t *samples, int length)
/* sample conversion relative to SPEECH level */
void samples_to_int16_speech(int16_t *spl, sample_t *samples, int length)
{
int32_t value;
@ -55,10 +57,33 @@ void samples_to_int16(int16_t *spl, sample_t *samples, int length)
}
}
void int16_to_samples(sample_t *samples, int16_t *spl, int length)
void int16_to_samples_speech(sample_t *samples, int16_t *spl, int length)
{
while (length--) {
*samples++ = (double)(*spl++) / 32767.0 / int_16_speech_level;
}
}
/* sample conversion relative to 1mW level */
void samples_to_int16_1mw(int16_t *spl, sample_t *samples, int length)
{
int32_t value;
while (length--) {
value = *samples++ * int_16_1mw_level * 32768.0;
if (value > 32767.0)
*spl++ = 32767;
else if (value < -32767.0)
*spl++ = -32767;
else
*spl++ = (uint16_t)value;
}
}
void int16_to_samples_1mw(sample_t *samples, int16_t *spl, int length)
{
while (length--) {
*samples++ = (double)(*spl++) / 32767.0 / int_16_1mw_level;
}
}

View File

@ -3,6 +3,8 @@ typedef double sample_t;
#define SPEECH_LEVEL 0.1585
void samples_to_int16(int16_t *spl, sample_t *samples, int length);
void int16_to_samples(sample_t *samples, int16_t *spl, int length);
void samples_to_int16_speech(int16_t *spl, sample_t *samples, int length);
void int16_to_samples_speech(sample_t *samples, int16_t *spl, int length);
void samples_to_int16_1mw(int16_t *spl, sample_t *samples, int length);
void int16_to_samples_1mw(sample_t *samples, int16_t *spl, int length);

View File

@ -34,8 +34,11 @@ int init_samplerate(samplerate_t *state, double low_samplerate, double high_samp
abort();
}
iir_lowpass_init(&state->up.lp, filter_cutoff, high_samplerate, 2);
iir_lowpass_init(&state->down.lp, filter_cutoff, high_samplerate, 2);
state->filter_cutoff = filter_cutoff;
if (state->filter_cutoff) {
iir_lowpass_init(&state->up.lp, filter_cutoff, high_samplerate, 2);
iir_lowpass_init(&state->down.lp, filter_cutoff, high_samplerate, 2);
}
return 0;
}
@ -49,7 +52,8 @@ int samplerate_downsample(samplerate_t *state, sample_t *samples, int input_num)
sample_t last_sample;
/* filter down */
iir_process(&state->down.lp, samples, input_num);
if (state->filter_cutoff)
iir_process(&state->down.lp, samples, input_num);
/* get last sample for interpolation */
last_sample = state->down.last_sample;
@ -94,15 +98,60 @@ int samplerate_downsample(samplerate_t *state, sample_t *samples, int input_num)
return output_num;
}
/* convert low sample rate to high sample rate */
int samplerate_upsample(samplerate_t *state, sample_t *input, int input_num, sample_t *output)
int samplerate_upsample_input_num(samplerate_t *state, int output_num)
{
int output_num = 0, i, idx;
double factor = 1.0 / state->factor, in_index, diff;
sample_t buff[(int)((double)input_num / factor + 0.5) + 10]; /* add some safety */
sample_t *samples, last_sample;
double factor = 1.0 / state->factor, in_index;
int idx = 0;
/* count output */
in_index = state->up.in_index;
while (output_num--) {
/* increment input index */
in_index += factor;
/* count index on overflow */
if (in_index >= 1.0) {
idx++;
in_index -= 1.0;
}
}
return idx;
}
int samplerate_upsample_output_num(samplerate_t *state, int input_num)
{
double factor = 1.0 / state->factor, in_index;
int output_num = 0, idx = 0;
/* count output */
in_index = state->up.in_index;
while (idx < input_num) {
/* count output number */
output_num++;
/* increment input index */
in_index += factor;
/* count index on overflow */
if (in_index >= 1.0) {
idx++;
in_index -= 1.0;
}
}
return output_num;
}
/* convert low sample rate to high sample rate */
void samplerate_upsample(samplerate_t *state, sample_t *input, int input_num, sample_t *output, int output_num)
{
int i, idx;
double factor = 1.0 / state->factor, in_index;
sample_t buff[output_num];
sample_t *samples, current_sample, last_sample;
/* get last sample for interpolation */
current_sample = state->up.current_sample;
last_sample = state->up.last_sample;
if (input == output)
@ -112,46 +161,41 @@ int samplerate_upsample(samplerate_t *state, sample_t *input, int input_num, sam
/* resample input */
in_index = state->up.in_index;
idx = 0;
for (i = 0; ; i++) {
/* convert index to int */
idx = (int)in_index;
/* if index is outside input sample range, we are done */
if (idx >= input_num)
break;
for (i = 0; i < output_num; i++) {
/* linear interpolation */
diff = in_index - (double)idx;
if (idx)
samples[i] = input[idx - 1] * (1.0 - diff) + input[idx] * diff;
else
samples[i] = last_sample * (1.0 - diff) + input[idx] * diff;
/* count output number */
output_num++;
samples[i] = last_sample * (1.0 - in_index) + current_sample * in_index;
/* increment input index */
in_index += factor;
/* get next sample on overflow */
if (in_index >= 1.0) {
if (idx == input_num) {
fprintf(stderr, "Given input_num is too small, please fix!\n");
}
last_sample = current_sample;
current_sample = input[idx++];
in_index -= 1.0;
}
}
if (idx < input_num) {
fprintf(stderr, "Given input_num is too large, please fix!\n");
abort();
}
/* store last sample for interpolation */
if (input_num)
state->up.last_sample = input[input_num - 1];
/* remove number of input samples from index */
in_index -= (double)input_num;
/* in_index cannot be negative, except due to rounding error, so... */
if ((int)in_index < 0)
in_index = 0.0;
state->up.last_sample = last_sample;
state->up.current_sample = current_sample;
state->up.in_index = in_index;
/* filter up */
iir_process(&state->up.lp, samples, output_num);
if (state->filter_cutoff)
iir_process(&state->up.lp, samples, output_num);
if (input == output) {
/* copy samples */
for (i = 0; i < output_num; i++)
*output++ = samples[i];
}
return output_num;
}

View File

@ -2,6 +2,7 @@
typedef struct samplerate {
double factor;
double filter_cutoff;
struct {
iir_filter_t lp;
sample_t last_sample;
@ -9,6 +10,7 @@ typedef struct samplerate {
} down;
struct {
iir_filter_t lp;
sample_t current_sample;
sample_t last_sample;
double in_index;
} up;
@ -16,4 +18,6 @@ typedef struct samplerate {
int init_samplerate(samplerate_t *state, double low_samplerate, double high_samplerate, double filter_cutoff);
int samplerate_downsample(samplerate_t *state, sample_t *samples, int input_num);
int samplerate_upsample(samplerate_t *state, sample_t *input, int input_num, sample_t *output);
int samplerate_upsample_input_num(samplerate_t *state, int output_num);
int samplerate_upsample_output_num(samplerate_t *state, int input_num);
void samplerate_upsample(samplerate_t *state, sample_t *input, int input_num, sample_t *output, int output_num);

View File

@ -85,7 +85,7 @@ int dsp_init_sender(mpt1327_t *mpt1327, double squelch_db)
mpt1327->dmp_frame_quality = display_measurements_add(&mpt1327->sender.dispmeas, "Frame Quality", "%.1f %% (last)", DISPLAY_MEAS_LAST, DISPLAY_MEAS_LEFT, 0.0, 100.0, 100.0);
/* repeater */
rc = jitter_create(&mpt1327->repeater_dejitter, mpt1327->sender.samplerate / 5);
rc = jitter_create(&mpt1327->repeater_dejitter, "repeater", mpt1327->sender.samplerate, sizeof(sample_t), 0.050, 0.500, JITTER_FLAG_NONE);
if (rc < 0) {
PDEBUG(DDSP, DEBUG_ERROR, "Failed to create and init repeater buffer!\n");
goto error;
@ -235,7 +235,7 @@ void sender_receive(sender_t *sender, sample_t *samples, int length, double __at
if (mpt1327->dsp_mode == DSP_MODE_TRAFFIC) {
/* if repeater mode, store sample in jitter buffer */
if (mpt1327->repeater)
jitter_save(&mpt1327->repeater_dejitter, samples, length);
jitter_save(&mpt1327->repeater_dejitter, samples, length, 0, 0, 0, 0);
if (mpt1327->unit && mpt1327->unit->callref) {
int count;
@ -279,6 +279,7 @@ static int fsk_send_bit(void *inst)
void sender_send(sender_t *sender, sample_t *samples, uint8_t *power, int length)
{
mpt1327_t *mpt1327 = (mpt1327_t *) sender;
int input_num;
if (mpt1327->dsp_mode == DSP_MODE_OFF) {
memset(power, 0, length);
@ -289,7 +290,9 @@ void sender_send(sender_t *sender, sample_t *samples, uint8_t *power, int length
memset(power, 1, length);
if (mpt1327->dsp_mode == DSP_MODE_TRAFFIC) {
jitter_load(&mpt1327->sender.dejitter, samples, length);
input_num = samplerate_upsample_input_num(&sender->srstate, length);
jitter_load(&sender->dejitter, samples, input_num);
samplerate_upsample(&sender->srstate, samples, input_num, samples, length);
/* if repeater mode, sum samples from jitter buffer to samples */
if (mpt1327->repeater) {
sample_t uplink[length];

View File

@ -1642,7 +1642,7 @@ void call_down_release(int callref, __attribute__((unused)) int cause)
}
/* Receive audio from call instance. */
void call_down_audio(int callref, sample_t *samples, int count)
void call_down_audio(int callref, uint16_t sequence, uint32_t timestamp, uint32_t ssrc, sample_t *samples, int count)
{
mpt1327_unit_t *unit;
@ -1652,11 +1652,8 @@ void call_down_audio(int callref, sample_t *samples, int count)
if (!unit->tc)
return;
if (unit->tc->state == STATE_BUSY && unit->tc->dsp_mode == DSP_MODE_TRAFFIC) {
sample_t up[(int)((double)count * unit->tc->sender.srstate.factor + 0.5) + 10];
count = samplerate_upsample(&unit->tc->sender.srstate, samples, count, up);
jitter_save(&unit->tc->sender.dejitter, up, count);
}
if (unit->tc->state == STATE_BUSY && unit->tc->dsp_mode == DSP_MODE_TRAFFIC)
jitter_save(&unit->tc->sender.dejitter, samples, count, 1, sequence, timestamp, ssrc);
}
void dump_info(void)

View File

@ -139,7 +139,7 @@ static struct nmt_country {
{ 450, 1, 7, 1,9, "PL", "Poland", "CENTERTEL", frq_450_scandinavia },
{ 450, 1, 6, 1,9, "BG", "Bulgaria", "MOBIFON", frq_450_scandinavia },
{ 450, 1, 5, 1,9, "RO", "Romania", "Telefonica Romania", frq_450_scandinavia },
{ 450, 1, 6, 1,9, "UA", "Ukraine", "Ukraine Mobile Comm.", frq_450_scandinavia },
{ 450, 1, 8, 1,9, "UA", "Ukraine", "Ukrainian Mobile Communications", frq_450_scandinavia },
{ 450, 1, 1, 1,9, "RU1", "", "", frq_450_scandinavia },
{ 450, 1, 2, 1,9, "RU2", "", "", frq_450_scandinavia },
{ 450, 1, 3, 1,9, "RU3", "", "", frq_450_scandinavia },

View File

@ -356,16 +356,20 @@ void sender_receive(sender_t *sender, sample_t *samples, int length, double __at
if ((nmt->dsp_mode == DSP_MODE_AUDIO || nmt->dsp_mode == DSP_MODE_DTMF)
&& nmt->trans && nmt->trans->callref) {
int count;
int len, count;
count = samplerate_downsample(&nmt->sender.srstate, samples, length);
len = samplerate_downsample(&nmt->sender.srstate, samples, length);
if (nmt->compandor)
expand_audio(&nmt->cstate, samples, count);
if (nmt->dsp_mode == DSP_MODE_DTMF)
dtmf_encode(&nmt->dtmf, samples, count);
expand_audio(&nmt->cstate, samples, len);
if (nmt->dsp_mode == DSP_MODE_DTMF) {
/* encode and fill with silence after finish */
count = dtmf_encode(&nmt->dtmf, samples, len);
if (count < len)
memset(samples + count, 0, sizeof(*samples) * (len - count));
}
spl = nmt->sender.rxbuf;
pos = nmt->sender.rxbuf_pos;
for (i = 0; i < count; i++) {
for (i = 0; i < len; i++) {
spl[pos++] = samples[i];
if (pos == 160) {
call_up_audio(nmt->trans->callref, spl, 160);
@ -446,7 +450,7 @@ static void dial_tone(nmt_t *nmt, sample_t *samples, int length)
void sender_send(sender_t *sender, sample_t *samples, uint8_t *power, int length)
{
nmt_t *nmt = (nmt_t *) sender;
int count;
int count, input_num;
memset(power, 1, length);
@ -454,7 +458,9 @@ again:
switch (nmt->dsp_mode) {
case DSP_MODE_AUDIO:
case DSP_MODE_DTMF:
jitter_load(&nmt->sender.dejitter, samples, length);
input_num = samplerate_upsample_input_num(&sender->srstate, length);
jitter_load(&sender->dejitter, samples, input_num);
samplerate_upsample(&sender->srstate, samples, input_num, samples, length);
/* send after dejitter, so audio is flushed */
if (nmt->dms.tx_frame_valid) {
fsk_mod_send(&nmt->fsk_mod, samples, length, 0);
@ -509,7 +515,7 @@ void nmt_set_dsp_mode(nmt_t *nmt, enum dsp_mode mode)
{
/* reset frame */
if (mode == DSP_MODE_FRAME && nmt->dsp_mode != mode) {
fsk_mod_tx_reset(&nmt->fsk_mod);
fsk_mod_reset(&nmt->fsk_mod);
nmt->tx_frame_length = 0;
}

View File

@ -315,6 +315,7 @@ static struct nmt_frame {
{ NMT_MESSAGE_4b, "NNNPYYJJJJJJJHHH", MTX_TO_MS, 7, "4b", "Access channel indication" },
{ NMT_MESSAGE_5a, "NNNPYYZXXXXXXLLL", MTX_TO_MS, 6, "5a", "Line signal" },
{ NMT_MESSAGE_5b, "NNNPYYZXXXXXXLQQ", MTX_TO_MS, 6, "5b", "Line signal: Answer to coin-box" },
{ NMT_MESSAGE_5c, "NNNPYYZXXXXXXFFF", MTX_TO_MS, 2, "5c", "Line signal: Message(s) waiting" },
{ NMT_MESSAGE_6, "JJJPJJJJJJJJJJJJ", MTX_TO_XX, 0, "6", "Idle frame" },
{ NMT_MESSAGE_7, "NNNPYYCCCCCCCJJJ", MTX_TO_MS, 8, "7", "Authentication request" },
{ NMT_MESSAGE_8, "NNNPYYMHHHHHHHWW", MTX_TO_MS, 1, "8", "A-subscriber number" },
@ -323,7 +324,7 @@ static struct nmt_frame {
{ NMT_MESSAGE_10c, "NNNPZXXXXXXTYKKK", MS_TO_MTX, 6, "10c", "Seizure and identity from called MS on traffic channel" },
{ NMT_MESSAGE_10d, "NNNPZXXXXXXTJJJJ", MS_TO_MTX, 6, "10d", "Call acknowledgement from MS on the alternative type of call on combined CC/TC (shortened frame)" },
{ NMT_MESSAGE_11a, "NNNPZXXXXXXTYKKK", MS_TO_MTX, 14, "11a", "Roaming updating seizure and identity on traffic channel" },
{ NMT_MESSAGE_11b, "NNNPZXXXXXXTYKKK", MS_TO_MTX, 15, "11b", "Seizure and call achnowledgment on calling channel from MS with priority (shortened frame)" },
{ NMT_MESSAGE_11b, "NNNPZXXXXXXTYJJJ", MS_TO_MTX, 15, "11b", "Seizure and call achnowledgment on calling channel from MS with priority (shortened frame)" },
{ NMT_MESSAGE_12, "NNNPZXXXXXXTYKKK", MS_TO_MTX, 11, "12", "Seizure from coin-box on traffic channel" },
{ NMT_MESSAGE_13a, "NNNPZXXXXXXLLLLL", MS_TO_MTX, 8, "13a", "Line signal" },
{ NMT_MESSAGE_13b, "NNNPZXXXXXXLLLQQ", MS_TO_MTX, 8, "13b", "Line signal: Answer acknowledgment from coin box" },
@ -348,7 +349,7 @@ static struct nmt_frame {
{ NMT_MESSAGE_28, "NNNPZJJVVVVJJJJJ", BS_TO_MTX, 13, "28", "Other maintenance information from BS" },
{ NMT_MESSAGE_30, "NNNPYYJJJJJJJHHH", MTX_TO_MS, 10, "30", "Test channel indication" },
{ NMT_MESSAGE_UKN_MTX, "---P------------", MTX_TO_XX, 0, "", "illegal (Spare)" },
{ NMT_MESSAGE_UKN_B, "---P------------", XX_TO_MTX, 0, "", "illegal (Spare)" },
{ NMT_MESSAGE_UKN_BS_MS,"---P------------", XX_TO_MTX, 0, "", "illegal (Spare)" },
{ 0, NULL, 0, 0, NULL, NULL }
};
@ -619,6 +620,27 @@ static const char *param_password(uint64_t value, int ndigits, enum nmt_directio
return result;
}
static const char *param_waiting(uint64_t value, int __attribute__((unused)) ndigits, enum nmt_direction __attribute__((unused)) direction)
{
static char result[128];
if (value & 0x01)
strcat(result, "SMS ");
if (value & 0x02)
strcat(result, "voice-mail ");
if (value & 0x04)
strcat(result, "fax ");
if (value & 0x08)
strcat(result, "e-mail ");
if (value & 0x10)
strcat(result, "data ");
if (value & 0x20)
strcat(result, "(spare) ");
strcat(result, " is waiting");
return result;
}
static struct nmt_parameter {
int system;
char digit;
@ -633,7 +655,7 @@ static struct nmt_parameter {
{ 900, 'Y', "Traffic area", param_ta_900 },
{ 0, 'Z', "Mobile subscriber country", param_country },
{ 0, 'X', "Mobile subscriber No.", param_number },
{ 0, 'Q', "Tariff class", param_integer },
{ 0, 'Q', "Tariff class", param_hex },
{ 0, 'L', "Line signal", param_line_signal },
{ 0, 'S', "Digit signals", param_digit },
{ 0, 'J', "Idle information", param_hex },
@ -651,6 +673,7 @@ static struct nmt_parameter {
{ 0, 'c', "c", param_hex },
{ 0, 'M', "Sequence Number", param_integer },
{ 0, 'W', "Checksum", param_hex },
{ 0, 'F', "Addit. Info.", param_waiting },
{ 0, 0, NULL, NULL }
};
@ -714,7 +737,7 @@ enum nmt_mt decode_frame_mt(const uint8_t *digits, enum nmt_direction direction,
case 15:
return NMT_MESSAGE_11b;
}
return NMT_MESSAGE_UKN_B;
return NMT_MESSAGE_UKN_BS_MS;
} else {
/* MTX to MS/BS */
switch (digits[3]) {
@ -723,7 +746,7 @@ enum nmt_mt decode_frame_mt(const uint8_t *digits, enum nmt_direction direction,
case 1:
return NMT_MESSAGE_8;
case 2:
break;
return NMT_MESSAGE_5c;
case 3:
if (digits[6] == 15)
return NMT_MESSAGE_21b;
@ -956,6 +979,9 @@ static void disassemble_frame(int nmt_system, frame_t *frame, const uint8_t *dig
case 'W':
frame->checksum = value;
break;
case 'F':
frame->waiting_info = value;
break;
default:
PDEBUG(DFRAME, DEBUG_ERROR, "Digit '%c' does not exist, please fix!\n", digit);
abort();
@ -1083,6 +1109,9 @@ static void assemble_frame(int nmt_system, frame_t *frame, uint8_t *digits, int
case 'W':
value = frame->checksum;
break;
case 'F':
value = frame->waiting_info;
break;
default:
PDEBUG(DFRAME, DEBUG_ERROR, "Digit '%c' does not exist, please fix!\n", digit);
abort();

Some files were not shown because too many files have changed in this diff Show More