AMPS: Caller ID Support

Support for sending caller ID for newer phones.
Currently does not work with older phones, they will abort.
This commit is contained in:
Andreas Eversberg 2017-05-22 18:02:07 +02:00
parent 1a0813069f
commit 4e669ecf79
7 changed files with 237 additions and 72 deletions

View File

@ -61,7 +61,9 @@
#define PAGE_TRIES 2 /* how many times to page the phone */ #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_TO1 8.0 /* max time to wait for paging reply */
#define PAGE_TO2 4.0 /* max time to wait for last 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.3 /* 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 */ #define RELEASE_TIMER 5.0 /* max time to send release messages */
/* Convert channel number to frequency number of base station. /* Convert channel number to frequency number of base station.
@ -502,7 +504,7 @@ static amps_t *search_pc(void)
} }
/* Create transceiver instance and link to a list. */ /* 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; sender_t *sender;
amps_t *amps; amps_t *amps;
@ -582,12 +584,12 @@ int amps_create(const char *kanal, enum amps_chan_type chan_type, const char *de
amps->chan_type = chan_type; amps->chan_type = chan_type;
memcpy(&amps->si, si, sizeof(amps->si)); memcpy(&amps->si, si, sizeof(amps->si));
amps->sat = sat; amps->sat = sat;
amps->send_callerid = send_callerid;
if (polarity < 0) if (polarity < 0)
amps->flip_polarity = 1; amps->flip_polarity = 1;
amps->pre_emphasis = pre_emphasis; amps->pre_emphasis = pre_emphasis;
amps->de_emphasis = de_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 */ /* 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); rc = init_emphasis(&amps->estate, samplerate, CUT_OFF_EMPHASIS_DEFAULT, CUT_OFF_HIGHPASS_DEFAULT, CUT_OFF_LOWPASS_DEFAULT);
if (rc < 0) if (rc < 0)
@ -688,14 +690,13 @@ static void amps_release(transaction_t *trans, uint8_t cause)
trans->callref = 0; trans->callref = 0;
} }
/* change DSP mode to transmit release */ /* 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); amps_set_dsp_mode(amps, DSP_MODE_AUDIO_RX_FRAME_TX, 0);
} }
/* /*
* receive signaling * receive signaling
*/ */
void amps_rx_signaling_tone(amps_t *amps, int tone, double quality) void amps_rx_signaling_tone(amps_t *amps, int tone, double quality)
{ {
transaction_t *trans = amps->trans_list; transaction_t *trans = amps->trans_list;
@ -710,6 +711,7 @@ void amps_rx_signaling_tone(amps_t *amps, int tone, double quality)
PDEBUG_CHAN(DAMPS, DEBUG_INFO, "Lost Signaling Tone signal\n"); PDEBUG_CHAN(DAMPS, DEBUG_INFO, "Lost Signaling Tone signal\n");
switch (trans->state) { switch (trans->state) {
case TRANS_CALL_MO_ASSIGN_CONFIRM: // should not happen
case TRANS_CALL: case TRANS_CALL:
if (!tone) if (!tone)
break; break;
@ -723,16 +725,19 @@ void amps_rx_signaling_tone(amps_t *amps, int tone, double quality)
destroy_transaction(trans); destroy_transaction(trans);
amps_go_idle(amps); amps_go_idle(amps);
break; 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) { if (tone) {
timer_stop(&trans->timer); timer_stop(&trans->timer);
call_up_alerting(trans->callref); call_up_alerting(trans->callref);
amps_set_dsp_mode(amps, DSP_MODE_AUDIO_RX_AUDIO_TX, 0); amps_set_dsp_mode(amps, DSP_MODE_AUDIO_RX_AUDIO_TX, 0);
trans_new_state(trans, TRANS_CALL_MT_ALERT_SEND); trans_new_state(trans, TRANS_CALL_MT_ANSWER_WAIT);
timer_start(&trans->timer, ALERT_TO); timer_start(&trans->timer, ANSWER_TO);
} }
break; break;
case TRANS_CALL_MT_ALERT_SEND: case TRANS_CALL_MT_ANSWER_WAIT:
if (!tone) { if (!tone) {
timer_stop(&trans->timer); timer_stop(&trans->timer);
if (!trans->sat_detected) if (!trans->sat_detected)
@ -753,13 +758,20 @@ void amps_rx_sat(amps_t *amps, int tone, double quality)
PDEBUG_CHAN(DAMPS, DEBUG_ERROR, "SAT signal without transaction, please fix!\n"); PDEBUG_CHAN(DAMPS, DEBUG_ERROR, "SAT signal without transaction, please fix!\n");
return; return;
} }
/* irgnoring SAT loss on release */ /* irgnoring SAT loss on release */
if (trans->state == TRANS_CALL_RELEASE if (trans->state == TRANS_CALL_RELEASE
|| trans->state == TRANS_CALL_RELEASE_SEND) || trans->state == TRANS_CALL_RELEASE_SEND)
return; 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
&& 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"); PDEBUG_CHAN(DAMPS, DEBUG_ERROR, "SAT signal without active call, please fix!\n");
return; return;
} }
@ -772,9 +784,26 @@ void amps_rx_sat(amps_t *amps, int tone, double quality)
trans->sat_detected = 0; trans->sat_detected = 0;
} }
/* no SAT during alerting */ /* 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);
}
/* no SAT timeout handling during alerting */
if (trans->state == TRANS_CALL_MT_ALERT if (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)
return; return;
if (tone) { if (tone) {
@ -790,20 +819,6 @@ void amps_rx_sat(amps_t *amps, int tone, double quality)
return; 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 */ /* 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) 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 +965,11 @@ int call_down_setup(int callref, const char __attribute__((unused)) *caller_id,
} }
trans->callref = callref; trans->callref = callref;
trans->page_retry = 1; 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; return 0;
} }
@ -988,8 +1008,11 @@ void call_down_disconnect(int callref, int cause)
switch (amps->dsp_mode) { switch (amps->dsp_mode) {
case DSP_MODE_AUDIO_RX_AUDIO_TX: case DSP_MODE_AUDIO_RX_AUDIO_TX:
case DSP_MODE_AUDIO_RX_FRAME_TX: case DSP_MODE_AUDIO_RX_FRAME_TX:
if (trans->state == TRANS_CALL_MT_ALERT if (trans->state == TRANS_CALL_MT_ASSIGN_CONFIRM
|| trans->state == TRANS_CALL_MT_ALERT_SEND) { || 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"); PDEBUG_CHAN(DAMPS, DEBUG_INFO, "Call control disconnect on voice channel while alerting, releasing towards mobile station.\n");
amps_release(trans, cause); amps_release(trans, cause);
} }
@ -1028,6 +1051,7 @@ void call_down_release(int callref, int cause)
trans->callref = 0; trans->callref = 0;
switch (amps->dsp_mode) { switch (amps->dsp_mode) {
case DSP_MODE_AUDIO_RX_SILENCE_TX:
case DSP_MODE_AUDIO_RX_AUDIO_TX: case DSP_MODE_AUDIO_RX_AUDIO_TX:
case DSP_MODE_AUDIO_RX_FRAME_TX: case DSP_MODE_AUDIO_RX_FRAME_TX:
PDEBUG_CHAN(DAMPS, DEBUG_INFO, "Call control releases on voice channel, releasing towards mobile station.\n"); PDEBUG_CHAN(DAMPS, DEBUG_INFO, "Call control releases on voice channel, releasing towards mobile station.\n");
@ -1071,8 +1095,16 @@ void transaction_timeout(struct timer *timer)
amps_t *amps = trans->amps; amps_t *amps = trans->amps;
switch (trans->state) { 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: 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; break;
case TRANS_CALL_RELEASE: case TRANS_CALL_RELEASE:
case TRANS_CALL_RELEASE_SEND: case TRANS_CALL_RELEASE_SEND:
@ -1080,10 +1112,18 @@ void transaction_timeout(struct timer *timer)
destroy_transaction(trans); destroy_transaction(trans);
amps_go_idle(amps); amps_go_idle(amps);
break; break;
case TRANS_CALL_MT_ALERT:
amps_release(trans, CAUSE_TEMPFAIL);
break;
case TRANS_CALL_MT_ALERT_SEND: 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"); PDEBUG_CHAN(DAMPS, DEBUG_NOTICE, "Alerting timeout, destroying transaction\n");
amps_release(trans, CAUSE_NOANSWER); amps_release(trans, CAUSE_NOANSWER);
break; break;
@ -1173,8 +1213,9 @@ again:
vc = assign_voice_channel(trans); vc = assign_voice_channel(trans);
if (vc) { if (vc) {
PDEBUG_CHAN(DAMPS, DEBUG_INFO, "Assignment complete, voice connected\n"); PDEBUG_CHAN(DAMPS, DEBUG_INFO, "Assignment complete, voice connected\n");
trans_new_state(trans, TRANS_CALL); /* timer and other things are processed at assign_voice_channel() */
amps_set_dsp_mode(vc, DSP_MODE_AUDIO_RX_AUDIO_TX, 0); trans_new_state(trans, TRANS_CALL_MO_ASSIGN_CONFIRM);
amps_set_dsp_mode(vc, DSP_MODE_AUDIO_RX_SILENCE_TX, 0);
} }
return NULL; return NULL;
case TRANS_CALL_MT_ASSIGN: case TRANS_CALL_MT_ASSIGN:
@ -1184,13 +1225,10 @@ again:
case TRANS_CALL_MT_ASSIGN_SEND: case TRANS_CALL_MT_ASSIGN_SEND:
vc = assign_voice_channel(trans); vc = assign_voice_channel(trans);
if (vc) { if (vc) {
PDEBUG_CHAN(DAMPS, DEBUG_INFO, "Assignment complete, next: sending alerting on VC\n"); PDEBUG_CHAN(DAMPS, DEBUG_INFO, "Assignment complete, waiting for SAT on VC\n");
trans->chan = 0; /* timer and other things are processed at assign_voice_channel() */
trans->msg_type = 0; trans_new_state(trans, TRANS_CALL_MT_ASSIGN_CONFIRM);
trans->ordq = 0; amps_set_dsp_mode(vc, DSP_MODE_AUDIO_RX_SILENCE_TX, 0);
trans->order = 1;
trans_new_state(trans, TRANS_CALL_MT_ALERT);
amps_set_dsp_mode(vc, DSP_MODE_AUDIO_RX_FRAME_TX, 0);
} }
return NULL; return NULL;
case TRANS_PAGE: case TRANS_PAGE:
@ -1223,8 +1261,25 @@ transaction_t *amps_tx_frame_fvc(amps_t *amps)
PDEBUG_CHAN(DAMPS, DEBUG_INFO, "Release call was sent, continue sending release\n"); PDEBUG_CHAN(DAMPS, DEBUG_INFO, "Release call was sent, continue sending release\n");
return trans; return trans;
case TRANS_CALL_MT_ALERT: 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) {
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; 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: default:
return NULL; return NULL;
} }

View File

@ -10,6 +10,7 @@ enum dsp_mode {
DSP_MODE_OFF, /* channel not active (VC) */ DSP_MODE_OFF, /* channel not active (VC) */
DSP_MODE_AUDIO_RX_AUDIO_TX, /* stream audio */ DSP_MODE_AUDIO_RX_AUDIO_TX, /* stream audio */
DSP_MODE_AUDIO_RX_FRAME_TX, /* stream audio, send frames */ 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 */ DSP_MODE_FRAME_RX_FRAME_TX, /* send and decode frames */
}; };
@ -56,6 +57,7 @@ struct amps {
/* system info */ /* system info */
amps_si si; amps_si si;
int send_callerid; /* if set, caller ID is transmitted */
/* cell nr selection */ /* cell nr selection */
int cell_auto; /* if set, cell_nr is selected automatically */ 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_msg_type; /* message (3 values) */
uint8_t tx_fvc_ordq; uint8_t tx_fvc_ordq;
uint8_t tx_fvc_order; 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 */ /* SAT tone */
int sat; /* use SAT tone 0..2 */ int sat; /* use SAT tone 0..2 */
int sat_samples; /* number of samples in buffer for supervisory detection */ 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); 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_min2number(uint32_t min1, uint16_t min2);
const char *amps_scm(uint8_t scm); 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_destroy(sender_t *sender);
void amps_go_idle(amps_t *amps); void amps_go_idle(amps_t *amps);
void amps_rx_signaling_tone(amps_t *amps, int tone, double quality); void amps_rx_signaling_tone(amps_t *amps, int tone, double quality);

View File

@ -38,7 +38,10 @@
int num_chan_type = 0; int num_chan_type = 0;
enum amps_chan_type chan_type[MAX_SENDER] = { CHAN_TYPE_CC_PC_VC }; enum amps_chan_type chan_type[MAX_SENDER] = { CHAN_TYPE_CC_PC_VC };
const char *flip_polarity = ""; 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; int tolerant = 0;
void print_help(const char *arg0) void print_help(const char *arg0)
@ -63,6 +66,12 @@ void print_help(const char *arg0)
printf(" Give DTX parameter for Discontinuous Transmission. (default = '%d')\n", dtx); printf(" Give DTX parameter for Discontinuous Transmission. (default = '%d')\n", dtx);
printf(" 0 = disable DTX; 1 = reserved;\n"); printf(" 0 = disable DTX; 1 = reserved;\n");
printf(" 2 = 8 dB attenuation in low state; 3 = transmitter off\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) { if (!tacs) {
printf(" -S --sysinfo sid=<System ID> | sid=list\n"); printf(" -S --sysinfo sid=<System ID> | sid=list\n");
printf(" Give system ID of cell broadcast\n"); printf(" Give system ID of cell broadcast\n");
@ -110,6 +119,7 @@ static void add_options(void)
option_add('F', "flip-polarity", 1); option_add('F', "flip-polarity", 1);
option_add('P', "ms-power", 1); option_add('P', "ms-power", 1);
option_add('D', "dtx", 1); option_add('D', "dtx", 1);
option_add('I', "caller-id", 1);
option_add('S', "sysinfo", 1); option_add('S', "sysinfo", 1);
option_add('O', "tolerant", 0); option_add('O', "tolerant", 0);
} }
@ -156,6 +166,9 @@ static int handle_options(int short_option, int argi, char **argv)
if (dtx < 0) if (dtx < 0)
dtx = 0; dtx = 0;
break; break;
case 'I':
send_callerid = atoi(argv[argi]);
break;
case 'S': case 'S':
p = strchr(argv[argi], '='); p = strchr(argv[argi], '=');
if (!p) { if (!p) {
@ -384,7 +397,7 @@ int main_amps_tacs(const char *name, int argc, char *argv[])
amps_si si; amps_si si;
init_sysinfo(&si, ms_power, ms_power, dtx, dcc, sid >> 1, regh, regr, pureg, pdreg, locaid, regincr, bis); 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) { if (rc < 0) {
fprintf(stderr, "Failed to create \"Sender\" instance. Quitting!\n"); fprintf(stderr, "Failed to create \"Sender\" instance. Quitting!\n");
goto fail; goto fail;

View File

@ -399,8 +399,8 @@ again:
rc = amps_encode_frame_fvc(amps, amps->fsk_tx_frame); rc = amps_encode_frame_fvc(amps, amps->fsk_tx_frame);
else else
rc = amps_encode_frame_focc(amps, amps->fsk_tx_frame); rc = amps_encode_frame_focc(amps, amps->fsk_tx_frame);
/* check if we have not bit string (change to tx audio) /* 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 achange */ * we may not store fsk_tx_buffer_pos, because is was reset on a mode change */
if (rc) if (rc)
return count; return count;
amps->fsk_tx_frame_pos = 0; amps->fsk_tx_frame_pos = 0;
@ -487,7 +487,13 @@ again:
/* pre-emphasis */ /* pre-emphasis */
if (amps->pre_emphasis) if (amps->pre_emphasis)
pre_emphasis(&amps->estate, samples, length); 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); sat_encode(amps, samples, length);
break; break;
case DSP_MODE_AUDIO_RX_FRAME_TX: case DSP_MODE_AUDIO_RX_FRAME_TX:
@ -496,6 +502,7 @@ again:
* stopped, process again for rest of stream. */ * stopped, process again for rest of stream. */
count = fsk_frame(amps, samples, length); count = fsk_frame(amps, samples, length);
memset(power, 1, count); memset(power, 1, count);
// no SAT during frame transmission, according to specs
samples += count; samples += count;
power += count; power += count;
length -= count; length -= count;
@ -894,6 +901,7 @@ void sender_receive(sender_t *sender, sample_t *samples, int length, double __at
break; break;
case DSP_MODE_AUDIO_RX_AUDIO_TX: case DSP_MODE_AUDIO_RX_AUDIO_TX:
case DSP_MODE_AUDIO_RX_FRAME_TX: case DSP_MODE_AUDIO_RX_FRAME_TX:
case DSP_MODE_AUDIO_RX_SILENCE_TX:
sender_receive_audio(amps, samples, length); sender_receive_audio(amps, samples, length);
break; break;
} }
@ -923,13 +931,13 @@ void amps_set_dsp_mode(amps_t *amps, enum dsp_mode mode, int frame_length)
amps->tx_focc_debugged = 0; amps->tx_focc_debugged = 0;
} }
if (amps->dsp_mode == DSP_MODE_FRAME_RX_FRAME_TX 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 */ /* reset SAT detection */
sat_reset(amps, "Change from FOCC to FVC"); sat_reset(amps, "Change from FOCC to FVC");
PDEBUG_CHAN(DDSP, DEBUG_INFO, "Change mode from FOCC to FVC\n"); PDEBUG_CHAN(DDSP, DEBUG_INFO, "Change mode from FOCC to FVC\n");
} }
if (amps->dsp_mode == DSP_MODE_OFF 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 */ /* reset SAT detection */
sat_reset(amps, "Enable FVC"); sat_reset(amps, "Enable FVC");
PDEBUG_CHAN(DDSP, DEBUG_INFO, "Change mode from OFF to FVC\n"); PDEBUG_CHAN(DDSP, DEBUG_INFO, "Change mode from OFF to FVC\n");
@ -940,6 +948,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_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; amps->dsp_mode = mode;
if (frame_length) if (frame_length)
amps->fsk_rx_frame_length = 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); 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 */ /* decoder function of a word */
static frame_t *amps_decode_word(uint64_t word, struct def_word *w) 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); memcpy(bits + 0, dotting, 10);
bits[10] = 'i'; bits[10] = 'i';
strcpy(bits + 11, sync_word); memcpy(bits + 11, sync_word, 11);
bits[22] = 'i'; bits[22] = 'i';
k = 23; k = 23;
for (i = 0; i < 5; i++) { 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) if (k != 463)
abort(); abort();
bits[463] = '\0'; bits[k] = '\0';
#ifdef BIT_DEBUGGING
if (debuglevel == DEBUG_DEBUG) { if (debuglevel == DEBUG_DEBUG) {
char text[64]; char text[64];
strncpy(text, bits, 23); strncpy(text, bits, 23);
text[23] = '\0'; text[23] = '\0';
#ifdef BIT_DEBUGGING
PDEBUG(DFRAME, DEBUG_INFO, "TX FOCC: %s\n", text); PDEBUG(DFRAME, DEBUG_INFO, "TX FOCC: %s\n", text);
for (i = 0; i < 10; i++) { for (i = 0; i < 10; i++) {
strncpy(text, bits + 23 + i * 44, 44); strncpy(text, bits + 23 + i * 44, 44);
text[44] = '\0'; text[44] = '\0';
PDEBUG(DFRAME, DEBUG_DEBUG, " word %c - %s\n", (i & 1) ? 'b' : 'a', text); 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) 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); memcpy(bits + k, dotting, 37);
k += 37; k += 37;
} }
strcpy(bits + k, sync_word); memcpy(bits + k, sync_word, 11);
k += 11; k += 11;
for (j = 39; j >= 0; j--) for (j = 39; j >= 0; j--)
bits[k++] = ((word_a >> j) & 1) + '0'; bits[k++] = ((word_a >> j) & 1) + '0';
} }
if (k != 1032) if (k != 1032)
abort(); abort();
bits[k] = '\0';
bits[1032] = '\0';
#ifdef BIT_DEBUGGING #ifdef BIT_DEBUGGING
if (debuglevel == DEBUG_DEBUG) { 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_ordq = trans->ordq;
amps->tx_fvc_order = trans->order; amps->tx_fvc_order = trans->order;
amps->tx_fvc_chan = trans->chan; 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_send = 1;
amps->tx_fvc_word_count = 0;
amps->tx_fvc_word_repeat = 0;
} }
/* on change of dsp mode */ /* on change of dsp mode */
if (amps->dsp_mode != DSP_MODE_AUDIO_RX_FRAME_TX) 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 */ /* send scheduled mobile control message */
if (amps->tx_fvc_send) { if (amps->tx_fvc_send) {
amps->tx_fvc_send = 0; if (amps->tx_fvc_word_count == 0) {
if (amps->tx_fvc_chan) 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); 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 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); 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 } else
return 1; return 1;

View File

@ -37,17 +37,25 @@ static const char *trans_state_name(int state)
case TRANS_REGISTER_ACK_SEND: case TRANS_REGISTER_ACK_SEND:
return "REGISTER ACK SEND"; return "REGISTER ACK SEND";
case TRANS_CALL_MO_ASSIGN: case TRANS_CALL_MO_ASSIGN:
return "CALL ASSIGN MOBILE ORIGINATING"; return "MO CALL ASSIGNMENT";
case TRANS_CALL_MO_ASSIGN_SEND: 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: case TRANS_CALL_MT_ASSIGN:
return "CALL ASSIGN MOBILE TERMINATING"; return "MT CALL ASSIGNMENT";
case TRANS_CALL_MT_ASSIGN_SEND: 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: case TRANS_CALL_MT_ALERT:
return "CALL ALERT MOBILE TERMINATING"; return "MT CALL ALERT";
case TRANS_CALL_MT_ALERT_SEND: 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: case TRANS_CALL_REJECT:
return "CALL REJECT"; return "CALL REJECT";
case TRANS_CALL_REJECT_SEND: case TRANS_CALL_REJECT_SEND:
@ -79,11 +87,15 @@ const char *trans_short_state_name(int state)
return "REGISTER"; return "REGISTER";
case TRANS_CALL_MO_ASSIGN: case TRANS_CALL_MO_ASSIGN:
case TRANS_CALL_MO_ASSIGN_SEND: case TRANS_CALL_MO_ASSIGN_SEND:
case TRANS_CALL_MO_ASSIGN_CONFIRM:
case TRANS_CALL_MT_ASSIGN: case TRANS_CALL_MT_ASSIGN:
case TRANS_CALL_MT_ASSIGN_SEND: case TRANS_CALL_MT_ASSIGN_SEND:
case TRANS_CALL_MT_ASSIGN_CONFIRM:
return "ASSIGN"; return "ASSIGN";
case TRANS_CALL_MT_ALERT: case TRANS_CALL_MT_ALERT:
case TRANS_CALL_MT_ALERT_SEND: case TRANS_CALL_MT_ALERT_SEND:
case TRANS_CALL_MT_ALERT_CONFIRM:
case TRANS_CALL_MT_ANSWER_WAIT:
return "ALERT"; return "ALERT";
case TRANS_CALL_REJECT: case TRANS_CALL_REJECT:
case TRANS_CALL_REJECT_SEND: 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_REGISTER_ACK_SEND, /* attach request received, sending ack */
TRANS_CALL_MO_ASSIGN, /* assigning channel, waiting to send */ TRANS_CALL_MO_ASSIGN, /* assigning channel, waiting to send */
TRANS_CALL_MO_ASSIGN_SEND, /* assigning channel, sending assignment */ 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, /* assigning channel, waiting to send */
TRANS_CALL_MT_ASSIGN_SEND, /* assigning channel, sending assignment */ 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_ASSIGN_CONFIRM, /* assignment sent, waiting for confirm (SAT) */
TRANS_CALL_MT_ALERT_SEND, /* ringing the phone, signaling tone is received */ 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, /* rejecting channel, waiting to send */
TRANS_CALL_REJECT_SEND, /* rejecting channel, sending reject */ TRANS_CALL_REJECT_SEND, /* rejecting channel, sending reject */
TRANS_CALL, /* active call */ TRANS_CALL, /* active call */
@ -31,6 +35,8 @@ typedef struct transaction {
uint8_t ordq; uint8_t ordq;
uint8_t order; uint8_t order;
uint16_t chan; /* channel to assign */ 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 */ char dialing[33]; /* number dialed by the phone */
enum amps_trans_state state; /* state of transaction */ enum amps_trans_state state; /* state of transaction */
struct timer timer; /* for varous timeouts */ struct timer timer; /* for varous timeouts */