forked from pttlink/Asterisk
-
Notifications
You must be signed in to change notification settings - Fork 6
/
chan_voter.c
5142 lines (4807 loc) · 165 KB
/
chan_voter.c
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
752
753
754
755
756
757
758
759
760
761
762
763
764
765
766
767
768
769
770
771
772
773
774
775
776
777
778
779
780
781
782
783
784
785
786
787
788
789
790
791
792
793
794
795
796
797
798
799
800
801
802
803
804
805
806
807
808
809
810
811
812
813
814
815
816
817
818
819
820
821
822
823
824
825
826
827
828
829
830
831
832
833
834
835
836
837
838
839
840
841
842
843
844
845
846
847
848
849
850
851
852
853
854
855
856
857
858
859
860
861
862
863
864
865
866
867
868
869
870
871
872
873
874
875
876
877
878
879
880
881
882
883
884
885
886
887
888
889
890
891
892
893
894
895
896
897
898
899
900
901
902
903
904
905
906
907
908
909
910
911
912
913
914
915
916
917
918
919
920
921
922
923
924
925
926
927
928
929
930
931
932
933
934
935
936
937
938
939
940
941
942
943
944
945
946
947
948
949
950
951
952
953
954
955
956
957
958
959
960
961
962
963
964
965
966
967
968
969
970
971
972
973
974
975
976
977
978
979
980
981
982
983
984
985
986
987
988
989
990
991
992
993
994
995
996
997
998
999
1000
/*
* Asterisk -- An open source telephony toolkit.
*
* Copyright (C) 1999 - 2011, Digium, Inc.
*
* Copyright (C) 2011-2013
* Jim Dixon, WB6NIL <[email protected]>
*
* See http://www.asterisk.org for more information about
* the Asterisk project. Please do not directly contact
* any of the maintainers of this project for assistance;
* the project provides a web site, mailing lists and IRC
* channels for your use.
*
* This program is free software, distributed under the terms of
* the GNU General Public License Version 2. See the LICENSE file
* at the top of the source tree.
*/
/*! \file
*
* \brief Radio Voter channel driver for Asterisk
*
* \author Jim Dixon, WB6NIL <[email protected]>
*
* \ingroup channel_drivers
*/
/*** MODULEINFO
***/
/* Basic Information On How This Works
Each node has a number of potential "clients" associated with it. In the voter.conf file, each stanza (category)
is named by the node number that the clients specified within the stanza are to be associated with. Each entry
consists of an arbitrary (realtively meaningless, just included for easy identification putposes within this
channel driver, and has nothing to do with its operation) identifier equated to a unique password. This password
is programmed into the client. All clients must have unique passwords, as that is what is used by this channel
driver to identify them.
Each channel instance (as opened by app_rpt as a main radio channel, e.g. rxchannel=Voter/1999 in rpt.conf) and
is directly associated with the node that opened it.
Each client has a pair of circular buffers, one for mu-law audio data, and one for RSSI value. The allocated buffer
length in all clients is determined by the 'buflen' parameter, which is specified in the "global" stanza in the
voter.conf file in milliseconds, and represnted in the channel driver as number of samples (actual buffer length,
which is 8 * milliseconds).
Every channel instance has a index ("drainindex"), indicating the next position within the physical buffer(s) where
the audio will be taken from the buffers and presented to the Asterisk channel stream as VOICE frames.
Therefore, there is an abstraction of a "buffer" that exists starting at drainindex and ending (modulo) at
drainindex - 1, with length of buflen.
Buflen is selected so that there is enough time (delay) for any straggling packets to arrive before it is time
to present the data to the Asterisk channel.
The idea is that the current audio being presented to Asterisk is from some time shortly in the past. Therefore,
"Now" is the position in the abstratcted buffer of 'bufdelay' (generally buflen - 160) (you gotta at least leave room for
an entire frame) and the data is being presented from the start of the abstracted buffer. As the physical buffer
moves along, what was once "now" will eventually become far enough in the "past" to be presented to Asterisk (gosh,
doesn't this sound like a scene from "Spaceballs"??.. I too always drink coffee while watching "Mr. Radar").
During the processing of an audio frame to be presented to Asterisk, all client's buffers that are associated with
a channel instance (node) are examined by taking an average of the RSSI value for each sample in the associated
time period (the first 160 samples of the abstracted buffer (which is the physical buffer from drainindex to
drainindex + 159) and whichever one, if any that has the largest RSSI average greather then zero is selected
as the audio source for that frame. The corresponding audio buffer's contents (in the corresponding offsets)
are presented to Asterisk, then ALL the clients corresponding RSSI data is set to 0, ALL the clients corresponding
audio is set to quiet (0x7f). The overwriting of the buffers after their use/examination is done so that the
next time those positions in the physical buffer are examined, they will not contain any data that was not actually
put there, since all client's buffers are significant regardless of whether they were populated or not. This
allows for the true 'connectionless-ness' of this protocol implementation.
Voter Channel test modes:
0 - Normal voting operation
1 - Randomly pick which client of all that
are receving at the max rssi value to use.
> 1 - Cycle thru all the clients that are receiving
at the max rssi value with a cycle time of (test mode - 1)
frames. In other words, if you set it to 2, it will
change every single time. If you set it to 11, it will
change every 10 times. This is serious torture test.
Note on ADPCM functionality:
The original intent was to change this driver to use signed linear internally,
but after some thought, it was determined that it was prudent to continue using
mulaw as the "standard" internal audio format (with the understanding of the slight
degradation in dynamic range when using ADPCM resulting in doing so). This was
done becuase existing external entities (such as the recording files and the streaming
stuff) use mulaw as their transport, and changing all of that to signed linear would
be cumbersome, inefficient and undesirable.
Note on "Dynamic" client functionality:
DONT USE IT!!. It is intentionally *NOT* documented to encourage non-use of this
feature. It is for demo purposes *ONLY*. The chan_voter driver will *NOT* properly
perform reliably in a production environment if this option is used.
Redundant "Proxy" Mode:
A "Redundant" (backup) server may be set up, so that if the "primary" server fails,
clients can detect this failure, and connect to the designated "backup" (or "secondary")
server.
Needless to say, since Internet connectivity is not by any means guaranteed to be consistent,
it is possible for some clients to have working connectivity to the "primary" server and not
others, even though the "primary" server is functional.
If this was to occur, actual voting and/or simulcast clients would have a "broken" system
(being that all the clients need to be on the same server for any sort of functional operation).
To eliminate this possibility, functionality has been added so that a "secondary" server
will "proxy" (forward) all of its VOTER packets to the "primary" (if the "primary" is
on line), and the "primary" will generate all of the outbound VOTER packets, which (for clients
"connected" to the "secondary" server) get sent to the "secondary" server to distribution to
its clients.
This allows for a "unity" of all of the clients on a network, even though they may be connected
to different servers.
In addition, it is assumed that "permanent linking" (at least of some sort) will be provided between
the channel side of the chan_voter instances (presumably through a "perma-link" provided by app_rpt).
When the "secondary" is "proxying" (to the "primary") it does not provide direct connectivity to/from
its locally-connected clients, thus allowing them to "connect" via the "primary" server instead. In
"normal" mode, it works "normally".
The operation is performed by more-or-less "encapsulating" the VOTER packets received by the "secondary"
server, and forwarding them on to the "primary" server, where they are "un-encapsulated" and appear to
that serer to be coming from clients connected directly to it (and keeps track of which ones are connected
in this manner, etc). When it needs to send VOTER packets to a client connected through the "secondary",
it "encapsulates" them, and sends them to the "secondary", where they get "un-enacpsulated" and sent
to their associated connected clients, based upon information in the "encapsulation".
If the "secondary" server loses (or does not make) connection to the "primary", it operates as normal, until
such time as it can make the connection.
The server redundancy feature is local to each chan_voter instance.
For each chan_voter instance served by both the "primary" and "secondary" servers, the client
list (parameters, etc) *MUST* be identical.
In addition, the following things must be added uniquely on each server:
In the "primary" server, there needs to be a "primary connectivity" client specified for each
"secondary" server for which it is "primary". Basicaly, this is a client that does NOTHING other
then providing a means by which the "secondary" can determine whether the "primary" is on line.
It is a stanard chan_voter client, with nothing else specified other then its password. Again,
although it is a "legitmate" client (technically), its only purpose *MUST* be to allow the secondary
server to connect to it.
The "primary" server also needs to have the following in all of its instances that require redundancy:
isprimary = y
The "secondary" server needs to have the following in all of its instances that require redundancy:
primary = 12.34.56.78:667,mypswd
(where 12.34.56.78:667 is the IPADDDR:PORT of the "primary" server, and mypswd is the password of the
"primary connectivity" client)
Note: Master timing sources *MUST* be local to their associated server, and therefore, can not be operated
in a redundant configuration. If a radio needs server redundancy, it CAN NOT be connected to a master timing
source. Also, the master timing source MUST be associated with a chan_voter instance that DOES NOT have
redundancy configured for it, even if a separate instance needs to be created just for this purpose.
Also, if Non-GPS-based operation is all that is needed, just the use of redundancy within the clients is
sufficient, and does not require any use of the server redundancy features.
"hostdeemp" (app_rpt duplex=3) mode:
As of Voter board firmware 1.19 (7/19/2013), there is a set of options in both the firmware ("Offline Menu item
#12, "DUPLEX3 support"), and the "hostdeemp" option (instance-wide) in the voter.conf file on the host.
Duplex mode 3 in app_rpt allows for "in-cabinet" repeat audio (where the actual radio hardware supplies the repeated
audio directly itself, and app_rpt simply "adds" all of the other audio as appropriate.
The Voter board (RTCM) now has an option to do the same functionality itself, for a case where local repeat audio
is desired without the "network audio delay" normally associated with Voter board (RTCM) operation, and for a radio
that doesnt have the option of providing "in cabinet" repeat audio (along with externally provided audio) itself.
Because of limitations with the Voter board (RTCM) hardware (being that there is only 1 audio path into the processor,
and it either has de-emphasis in its "hardware path" of not), it is necessary if you:
1) Wish to have the "duplex=3" functionality in app_rpt
2) Have the "DUPLEX3" support enabled in the Voter (RTCM) board
3) Have a transmitter that you are "modulating directly" (with flat audio)
If all of the above is true, then you need to use the "hostdeemp" option in chan voter, which basically "forces" the
RTCM *NOT* to do de-emphasis in hardware (it will send the non-de-emphasized audio to the host), and have the host
"do" the de-emphasis (in software) instead.
This will allow the Voter (RTCM) board to be able to "pass" the non-de-emphaszed audio back into the "direct modulation
audio" stream, since that is what will be "presented" to the processor in the Voter (RTCM) board, as the hardware de-emphasis
is disabled in this mode.
If you have a transmitter that you are "feeding" line-level (mic) audio, then this mode is not necessary, as the Voter (RTCM)
board is fully capable of providing the functionality all by itself.
Obviously, it is not valid to use *ANY* of the duplex=3 modes in a voted and/or simulcasted system.
*/
#include "asterisk.h"
#include "../astver.h"
/*
* Please change this revision number when you make a edit
* use the simple format YYMMDD
*/
ASTERISK_FILE_VERSION(__FILE__, "$Revision: 180213 $")
// ASTERISK_FILE_VERSION(__FILE__, "$"ASTERISK_VERSION" $")
#include <stdio.h>
#include <string.h>
#include <sys/socket.h>
#include <sys/time.h>
#include <errno.h>
#include <unistd.h>
#include <stdlib.h>
#include <arpa/inet.h>
#include <fcntl.h>
#include <sys/ioctl.h>
#include <sys/timex.h>
#include <ctype.h>
#include <search.h>
#include <arpa/inet.h>
#include <netinet/in.h>
#include <netdb.h>
#include <pthread.h>
#include <signal.h>
#include <fnmatch.h>
#include <math.h>
#include "asterisk/lock.h"
#include "asterisk/channel.h"
#include "asterisk/config.h"
#include "asterisk/logger.h"
#include "asterisk/module.h"
#include "asterisk/pbx.h"
#include "asterisk/options.h"
#include "asterisk/utils.h"
#include "asterisk/app.h"
#include "asterisk/translate.h"
#include "asterisk/astdb.h"
#include "asterisk/cli.h"
#include "asterisk/ulaw.h"
#include "asterisk/dsp.h"
#include "asterisk/manager.h"
#include "../allstar/pocsag.c"
/* Un-comment this if you wish Digital milliwatt output rather then real audio
when transmitting (for debugging only) */
/* #define DMWDIAG */
#ifdef NEW_ASTERISK
struct ast_flags zeroflag = { 0 };
#endif
#define XPMR_VOTER
#include "xpmr/xpmr.h"
#ifdef OLD_ASTERISK
#define AST_MODULE_LOAD_DECLINE -1
#endif
#define VOTER_CHALLENGE_LEN 10
#define VOTER_PASSWORD_LEN 30
#define VOTER_NAME_LEN 50
#define RX_TIMEOUT_MS 200
#define CLIENT_TIMEOUT_MS 3000
#define MASTER_TIMEOUT_MS 100
#define TX_KEEPALIVE_MS 1000
#define PING_TIME_MS 250
#define PING_TIMEOUT_MS 3000
#define DEFAULT_LINGER 6
#define DEFAULT_GTXGAIN "0.0"
#define DEFAULT_DYNTIME 30000
#define MAX_MASTER_COUNT 3
#define CLIENT_WARN_SECS 60
#define DELIMCHR ','
#define QUOTECHR 34
#define MAXSTREAMS 50
#define MAXTHRESHOLDS 20
#define GPS_WORK_FILE "/tmp/gps%s.tmp"
#define GPS_DATA_FILE "/tmp/gps%s.dat"
#define NTAPS_PL 6
#define NTAPS_4K 6
#ifdef DMWDIAG
unsigned char ulaw_digital_milliwatt[8] = { 0x1e, 0x0b, 0x0b, 0x1e, 0x9e, 0x8b, 0x8b, 0x9e };
unsigned char mwp;
#endif
#define IS_CLIENT_PROXY(x) (x->proxy_sin.sin_family == AF_INET)
#define SEND_PRIMARY(x) (x->primary.sin_family == AF_INET)
#define PAGER_SRC "PAGER"
#define ENDPAGE_STR "ENDPAGE"
#define AMPVAL 30000
#define SAMPRATE 8000 // (Sample Rate)
#define DIVLCM 192000 // (Least Common Mult of 512,1200,2400,8000)
#define PREAMBLE_BITS 576
#define MESSAGE_BITS 544 // (17 * 32), 1 longword SYNC plus 16 longwords data
// Apparently we have to send "inverted".. probably because of inverting AMP in Voter board
#define ONEVAL AMPVAL
#define ZEROVAL -AMPVAL
#define DIVSAMP (DIVLCM / SAMPRATE)
static const char vdesc[] = "radio Voter channel driver";
static char type[] = "voter";
int run_forever = 1;
static int nullfd = -1;
AST_MUTEX_DEFINE_STATIC(voter_lock);
int16_t listen_port = 667; /* port to listen to UDP packets on */
int udp_socket = -1;
int voter_timing_fd = -1;
int voter_timing_count = 0;
int last_master_count = 0;
int dyntime = DEFAULT_DYNTIME;
int check_client_sanity = 1;
char challenge[VOTER_CHALLENGE_LEN];
char password[VOTER_PASSWORD_LEN];
char context[100];
double dnsec;
static pthread_t voter_reader_thread = 0;
static pthread_t voter_timer_thread = 0;
int maxpvtorder = 0;
#define FRAME_SIZE 160
#define ADPCM_FRAME_SIZE 163
#define DEFAULT_BUFLEN 480 /* 480ms default buffer len */
#define BUFDELAY(p) (p->buflen - (FRAME_SIZE * 2))
#pragma pack(push)
#pragma pack(1)
typedef struct {
uint32_t vtime_sec;
uint32_t vtime_nsec;
} VTIME;
typedef struct {
VTIME curtime;
uint8_t challenge[10];
uint32_t digest;
uint16_t payload_type;
} VOTER_PACKET_HEADER;
typedef struct {
char lat[9];
char lon[10];
char elev[7];
} VOTER_GPS;
typedef struct {
char name[32];
uint8_t audio[FRAME_SIZE];
uint8_t rssi;
} VOTER_REC;
typedef struct {
VTIME curtime;
uint8_t audio[FRAME_SIZE];
char str[152];
} VOTER_STREAM;
typedef struct {
uint32_t ipaddr;
uint16_t port;
uint16_t payload_type;
uint8_t flags;
char challenge[VOTER_CHALLENGE_LEN];
} VOTER_PROXY_HEADER;
#pragma pack(pop)
#define VOTER_PAYLOAD_NONE 0
#define VOTER_PAYLOAD_ULAW 1
#define VOTER_PAYLOAD_GPS 2
#define VOTER_PAYLOAD_ADPCM 3
#define VOTER_PAYLOAD_NULAW 4
#define VOTER_PAYLOAD_PING 5
#define VOTER_PAYLOAD_PROXY 0xf000
struct voter_client {
uint32_t nodenum;
uint32_t digest;
char name[VOTER_NAME_LEN];
char pswd[VOTER_NAME_LEN];
uint8_t *audio;
uint8_t *rssi;
uint32_t respdigest;
struct sockaddr_in sin;
int drainindex;
int drainindex_40ms;
int buflen;
char heardfrom;
char totransmit;
char ismaster;
char curmaster;
char doadpcm;
char donulaw;
char mix;
char nodeemp;
char noplfilter;
char dynamic;
char txlockout;
struct voter_client *next;
uint8_t lastrssi;
int txseqno;
int txseqno_rxkeyed;
int rxseqno;
int rxseqno_40ms;
char rxseq40ms;
char drain40ms;
time_t warntime;
char *gpsid;
int reload;
int old_buflen;
struct timeval lastheardtime;
struct timeval lastdyntime;
struct timeval lastsenttime;
int prio;
int prio_override;
VTIME lastgpstime;
VTIME lastmastergpstime;
struct sockaddr_in proxy_sin;
char saved_challenge[VOTER_CHALLENGE_LEN];
short lastaudio[FRAME_SIZE];
struct timeval ping_txtime;
struct timeval ping_last_rxtime;
unsigned int ping_last_seqno;
int pings_requested;
int pings_sent;
int pings_received;
int pings_oos;
int pings_worst;
int pings_best;
unsigned int ping_seqno;
int pings_total_ms;
char ping_abort;
} ;
struct voter_pvt {
struct ast_channel *owner;
unsigned int nodenum; /* node # associated with instance */
struct voter_pvt *next;
struct ast_frame fr;
char buf[FRAME_SIZE + AST_FRIENDLY_OFFSET];
char txkey;
char rxkey;
struct ast_module_user *u;
struct timeval lastrxtime;
char drained_once;
int testcycle;
int testindex;
struct voter_client *lastwon;
char *streams[MAXSTREAMS];
int nstreams;
float hpx[NTAPS_PL + 1];
float hpy[NTAPS_PL + 1];
float rlpx[NTAPS_4K + 1];
float rlpy[NTAPS_4K + 1];
float tlpx[NTAPS_4K + 1];
float tlpy[NTAPS_4K + 1];
int32_t hdx;
char plfilter;
char hostdeemp;
int linger;
uint8_t rssi_thresh[MAXTHRESHOLDS];
uint16_t count_thresh[MAXTHRESHOLDS];
uint16_t linger_thresh[MAXTHRESHOLDS];
int nthresholds;
int threshold;
struct voter_client *winner;
uint16_t threshcount;
uint16_t lingercount;
struct ast_dsp *dsp;
struct ast_trans_pvt *adpcmin;
struct ast_trans_pvt *adpcmout;
struct ast_trans_pvt *nuin;
struct ast_trans_pvt *nuout;
struct ast_trans_pvt *toast;
struct ast_trans_pvt *toast1;
struct ast_trans_pvt *fromast;
t_pmr_chan *pmrChan;
char txctcssfreq[32];
int txctcsslevel;
int txctcsslevelset;
int txtoctype;
char duplex;
struct ast_frame *adpcmf1;
struct ast_frame *nulawf1;
ast_mutex_t xmit_lock;
ast_cond_t xmit_cond;
pthread_t xmit_thread;
int voter_test;
char usedtmf;
char isprimary;
char priconn;
struct sockaddr_in primary;
char primary_pswd[VOTER_NAME_LEN];
char primary_challenge[VOTER_CHALLENGE_LEN];
FILE *recfp;
short lastaudio[FRAME_SIZE];
char mixminus;
int order;
char waspager;
float gtxgain;
#ifdef OLD_ASTERISK
AST_LIST_HEAD(, ast_frame) txq;
#else
AST_LIST_HEAD_NOLOCK(, ast_frame) txq;
#endif
#ifdef OLD_ASTERISK
AST_LIST_HEAD(, ast_frame) pagerq;
#else
AST_LIST_HEAD_NOLOCK(, ast_frame) pagerq;
#endif
ast_mutex_t txqlock;
ast_mutex_t pagerqlock;
};
#ifdef OLD_ASTERISK
int reload();
#else
static int reload(void);
#endif
#ifdef OLD_ASTERISK
static int usecnt;
AST_MUTEX_DEFINE_STATIC(usecnt_lock);
#endif
int debug = 0;
int hasmaster = 0;
/* This is just a horrendous KLUDGE!! Some Garmin LVC-18 GPS "pucks"
*sometimes* get EXACTLY 1 second off!! Some dont do it at all.
Some do it constantly. Some just do it once in a while. In an attempt
to be at least somewhat tolerant of such swine poo-poo, the "puckit"
configuration flag may be set, which makes an attempt to deal with
this problem by keeping a "time differential" for each client (compared
with the "master") and applying it to time information within the protocol.
Obviously, this SHOULD NEVER HAVE TO BE DONE. */
int puckit = 0;
static char *config = "voter.conf";
struct voter_pvt *pvts = NULL;
struct voter_client *clients = NULL;
struct voter_client *dyn_clients = NULL;
FILE *fp;
VTIME master_time = {0,0};
VTIME mastergps_time = {0,0};
#ifdef OLD_ASTERISK
#define ast_free free
#define ast_malloc malloc
#endif
static struct ast_channel *voter_request(const char *type, int format, void *data, int *cause);
static int voter_call(struct ast_channel *ast, char *dest, int timeout);
static int voter_hangup(struct ast_channel *ast);
static struct ast_frame *voter_read(struct ast_channel *ast);
static int voter_write(struct ast_channel *ast, struct ast_frame *frame);
#ifdef OLD_ASTERISK
static int voter_indicate(struct ast_channel *ast, int cond);
static int voter_digit_end(struct ast_channel *c, char digit);
#else
static int voter_indicate(struct ast_channel *ast, int cond, const void *data, size_t datalen);
static int voter_digit_begin(struct ast_channel *c, char digit);
static int voter_digit_end(struct ast_channel *c, char digit, unsigned int duratiion);
#endif
static int voter_text(struct ast_channel *c, const char *text);
static int voter_setoption(struct ast_channel *chan, int option, void *data, int datalen);
static const struct ast_channel_tech voter_tech = {
.type = type,
.description = vdesc,
.capabilities = AST_FORMAT_SLINEAR,
.requester = voter_request,
.call = voter_call,
.hangup = voter_hangup,
.read = voter_read,
.write = voter_write,
.indicate = voter_indicate,
.send_text = voter_text,
#ifdef OLD_ASTERISK
.send_digit = voter_digit_end,
#else
.send_digit_begin = voter_digit_begin,
.send_digit_end = voter_digit_end,
.setoption = voter_setoption,
#endif
};
/*
* CLI extensions
*/
/* Debug mode */
static int voter_do_debug(int fd, int argc, char *argv[]);
static char debug_usage[] =
"Usage: voter debug level {0-7}\n"
" Enables debug messages in chan_voter\n";
/* Test */
static int voter_do_test(int fd, int argc, char *argv[]);
static char test_usage[] =
"Usage: voter test instance_id [test value]\n"
" Specifies/Queries test mode for voter instance\n";
/* Prio */
static int voter_do_prio(int fd, int argc, char *argv[]);
static char prio_usage[] =
"Usage: voter prio instance_id [client_id] [priority value]\n"
" Specifies/Queries priority value for voter client\n";
/* Record */
static int voter_do_record(int fd, int argc, char *argv[]);
static char record_usage[] =
"Usage: voter record instance_id [record filename]\n"
" Enables/Specifies (or disables) recording file for chan_voter\n";
/* Tone */
static int voter_do_tone(int fd, int argc, char *argv[]);
static char tone_usage[] =
"Usage: voter tone instance_id [new_tone_level(0-250)]\n"
" Sets/Queries Tx CTCSS level for specified chan_voter instance\n";
/* Reload */
static int voter_do_reload(int fd, int argc, char *argv[]);
static char reload_usage[] =
"Usage: voter reload\n"
" Reload chan_voter parameters\n";
/* Display */
static int voter_do_display(int fd, int argc, char *argv[]);
static char display_usage[] =
"Usage: voter display [instance]\n"
" Display voter instance clients\n";
/* Txlockout */
static int voter_do_txlockout(int fd, int argc, char *argv[]);
static char txlockout_usage[] =
"Usage: voter txlockout [instance] <client_list>\n"
" Set Tx Lockout for voter instance clients\n";
/* Ping client */
static int voter_do_ping(int fd, int argc, char *argv[]);
static char ping_usage[] =
"Usage: voter ping [client] <# pings, 0 to abort>\n"
" Ping (check connectivity) to client\n";
#ifndef NEW_ASTERISK
static struct ast_cli_entry cli_debug =
{ { "voter", "debug", "level" }, voter_do_debug,
"Enable voter debugging", debug_usage };
static struct ast_cli_entry cli_test =
{ { "voter", "test" }, voter_do_test,
"Specify/Query voter instance test mode", test_usage };
static struct ast_cli_entry cli_prio =
{ { "voter", "prio" }, voter_do_prio,
"Specify/Query voter client priority value", prio_usage };
static struct ast_cli_entry cli_record =
{ { "voter", "record" }, voter_do_record,
"Enables/Specifies (or disables) voter recording file", record_usage };
static struct ast_cli_entry cli_tone =
{ { "voter", "tone" }, voter_do_tone,
"Sets/Queries Tx CTCSS level for specified chan_voter instance", tone_usage };
static struct ast_cli_entry cli_reload =
{ { "voter", "reload" }, voter_do_reload,
"Reloads chan_voter parameters", reload_usage };
static struct ast_cli_entry cli_display =
{ { "voter", "display" }, voter_do_display,
"Display voter (instance) clients", display_usage };
static struct ast_cli_entry cli_txlockout =
{ { "voter", "txlockout" }, voter_do_txlockout,
"Set Tx Lockout status for voter (instance) clients", txlockout_usage };
static struct ast_cli_entry cli_ping =
{ { "voter", "ping" }, voter_do_ping,
"Ping (check connectivity) to client", ping_usage };
#endif
static uint32_t crc_32_tab[] = { /* CRC polynomial 0xedb88320 */
0x00000000, 0x77073096, 0xee0e612c, 0x990951ba, 0x076dc419, 0x706af48f,
0xe963a535, 0x9e6495a3, 0x0edb8832, 0x79dcb8a4, 0xe0d5e91e, 0x97d2d988,
0x09b64c2b, 0x7eb17cbd, 0xe7b82d07, 0x90bf1d91, 0x1db71064, 0x6ab020f2,
0xf3b97148, 0x84be41de, 0x1adad47d, 0x6ddde4eb, 0xf4d4b551, 0x83d385c7,
0x136c9856, 0x646ba8c0, 0xfd62f97a, 0x8a65c9ec, 0x14015c4f, 0x63066cd9,
0xfa0f3d63, 0x8d080df5, 0x3b6e20c8, 0x4c69105e, 0xd56041e4, 0xa2677172,
0x3c03e4d1, 0x4b04d447, 0xd20d85fd, 0xa50ab56b, 0x35b5a8fa, 0x42b2986c,
0xdbbbc9d6, 0xacbcf940, 0x32d86ce3, 0x45df5c75, 0xdcd60dcf, 0xabd13d59,
0x26d930ac, 0x51de003a, 0xc8d75180, 0xbfd06116, 0x21b4f4b5, 0x56b3c423,
0xcfba9599, 0xb8bda50f, 0x2802b89e, 0x5f058808, 0xc60cd9b2, 0xb10be924,
0x2f6f7c87, 0x58684c11, 0xc1611dab, 0xb6662d3d, 0x76dc4190, 0x01db7106,
0x98d220bc, 0xefd5102a, 0x71b18589, 0x06b6b51f, 0x9fbfe4a5, 0xe8b8d433,
0x7807c9a2, 0x0f00f934, 0x9609a88e, 0xe10e9818, 0x7f6a0dbb, 0x086d3d2d,
0x91646c97, 0xe6635c01, 0x6b6b51f4, 0x1c6c6162, 0x856530d8, 0xf262004e,
0x6c0695ed, 0x1b01a57b, 0x8208f4c1, 0xf50fc457, 0x65b0d9c6, 0x12b7e950,
0x8bbeb8ea, 0xfcb9887c, 0x62dd1ddf, 0x15da2d49, 0x8cd37cf3, 0xfbd44c65,
0x4db26158, 0x3ab551ce, 0xa3bc0074, 0xd4bb30e2, 0x4adfa541, 0x3dd895d7,
0xa4d1c46d, 0xd3d6f4fb, 0x4369e96a, 0x346ed9fc, 0xad678846, 0xda60b8d0,
0x44042d73, 0x33031de5, 0xaa0a4c5f, 0xdd0d7cc9, 0x5005713c, 0x270241aa,
0xbe0b1010, 0xc90c2086, 0x5768b525, 0x206f85b3, 0xb966d409, 0xce61e49f,
0x5edef90e, 0x29d9c998, 0xb0d09822, 0xc7d7a8b4, 0x59b33d17, 0x2eb40d81,
0xb7bd5c3b, 0xc0ba6cad, 0xedb88320, 0x9abfb3b6, 0x03b6e20c, 0x74b1d29a,
0xead54739, 0x9dd277af, 0x04db2615, 0x73dc1683, 0xe3630b12, 0x94643b84,
0x0d6d6a3e, 0x7a6a5aa8, 0xe40ecf0b, 0x9309ff9d, 0x0a00ae27, 0x7d079eb1,
0xf00f9344, 0x8708a3d2, 0x1e01f268, 0x6906c2fe, 0xf762575d, 0x806567cb,
0x196c3671, 0x6e6b06e7, 0xfed41b76, 0x89d32be0, 0x10da7a5a, 0x67dd4acc,
0xf9b9df6f, 0x8ebeeff9, 0x17b7be43, 0x60b08ed5, 0xd6d6a3e8, 0xa1d1937e,
0x38d8c2c4, 0x4fdff252, 0xd1bb67f1, 0xa6bc5767, 0x3fb506dd, 0x48b2364b,
0xd80d2bda, 0xaf0a1b4c, 0x36034af6, 0x41047a60, 0xdf60efc3, 0xa867df55,
0x316e8eef, 0x4669be79, 0xcb61b38c, 0xbc66831a, 0x256fd2a0, 0x5268e236,
0xcc0c7795, 0xbb0b4703, 0x220216b9, 0x5505262f, 0xc5ba3bbe, 0xb2bd0b28,
0x2bb45a92, 0x5cb36a04, 0xc2d7ffa7, 0xb5d0cf31, 0x2cd99e8b, 0x5bdeae1d,
0x9b64c2b0, 0xec63f226, 0x756aa39c, 0x026d930a, 0x9c0906a9, 0xeb0e363f,
0x72076785, 0x05005713, 0x95bf4a82, 0xe2b87a14, 0x7bb12bae, 0x0cb61b38,
0x92d28e9b, 0xe5d5be0d, 0x7cdcefb7, 0x0bdbdf21, 0x86d3d2d4, 0xf1d4e242,
0x68ddb3f8, 0x1fda836e, 0x81be16cd, 0xf6b9265b, 0x6fb077e1, 0x18b74777,
0x88085ae6, 0xff0f6a70, 0x66063bca, 0x11010b5c, 0x8f659eff, 0xf862ae69,
0x616bffd3, 0x166ccf45, 0xa00ae278, 0xd70dd2ee, 0x4e048354, 0x3903b3c2,
0xa7672661, 0xd06016f7, 0x4969474d, 0x3e6e77db, 0xaed16a4a, 0xd9d65adc,
0x40df0b66, 0x37d83bf0, 0xa9bcae53, 0xdebb9ec5, 0x47b2cf7f, 0x30b5ffe9,
0xbdbdf21c, 0xcabac28a, 0x53b39330, 0x24b4a3a6, 0xbad03605, 0xcdd70693,
0x54de5729, 0x23d967bf, 0xb3667a2e, 0xc4614ab8, 0x5d681b02, 0x2a6f2b94,
0xb40bbe37, 0xc30c8ea1, 0x5a05df1b, 0x2d02ef8d
};
static int32_t crc32_bufs(char *buf, char *buf1)
{
int32_t oldcrc32;
oldcrc32 = 0xFFFFFFFF;
while(buf && *buf)
{
oldcrc32 = crc_32_tab[(oldcrc32 ^ *buf++) & 0xff] ^ ((uint32_t)oldcrc32 >> 8);
}
while(buf1 && *buf1)
{
oldcrc32 = crc_32_tab[(oldcrc32 ^ *buf1++) & 0xff] ^ ((uint32_t)oldcrc32 >> 8);
}
return ~oldcrc32;
}
/* IIR 6 pole High pass filter, 300 Hz corner with 0.5 db ripple */
#define GAIN1 1.745882764e+00
static int16_t hpass6(int16_t input,float *xv,float *yv)
{
xv[0] = xv[1]; xv[1] = xv[2]; xv[2] = xv[3]; xv[3] = xv[4]; xv[4] = xv[5]; xv[5] = xv[6];
xv[6] = ((float)input) / GAIN1;
yv[0] = yv[1]; yv[1] = yv[2]; yv[2] = yv[3]; yv[3] = yv[4]; yv[4] = yv[5]; yv[5] = yv[6];
yv[6] = (xv[0] + xv[6]) - 6 * (xv[1] + xv[5]) + 15 * (xv[2] + xv[4])
- 20 * xv[3]
+ ( -0.3491861578 * yv[0]) + ( 2.3932556573 * yv[1])
+ ( -6.9905126572 * yv[2]) + ( 11.0685981760 * yv[3])
+ ( -9.9896695552 * yv[4]) + ( 4.8664511065 * yv[5]);
return((int)yv[6]);
}
/* IIR 6 pole Low pass filter, 1900 Hz corner with 0.5 db ripple */
#define GAIN2 1.080715413e+02
static int16_t lpass4(int16_t input,float *xv,float *yv)
{
xv[0] = xv[1]; xv[1] = xv[2]; xv[2] = xv[3]; xv[3] = xv[4]; xv[4] = xv[5]; xv[5] = xv[6];
xv[6] = ((float)input) / GAIN2;
yv[0] = yv[1]; yv[1] = yv[2]; yv[2] = yv[3]; yv[3] = yv[4]; yv[4] = yv[5]; yv[5] = yv[6];
yv[6] = (xv[0] + xv[6]) + 6 * (xv[1] + xv[5]) + 15 * (xv[2] + xv[4])
+ 20 * xv[3]
+ ( -0.1802140297 * yv[0]) + ( 0.7084527003 * yv[1])
+ ( -1.5847014566 * yv[2]) + ( 2.3188475168 * yv[3])
+ ( -2.5392334760 * yv[4]) + ( 1.6846484378 * yv[5]);
return((int)yv[6]);
}
/* FIR integrator providing de-emphasis @ 8000 samples/sec */
static int16_t deemp1(int16_t input, int32_t *state0)
{
int32_t accum;
int16_t output;
accum = input;
*state0 = accum + (*state0 * 25889) / M_Q15;
accum = (*state0 * 6878) / (M_Q15 / 4);
output = accum;
return(output);
}
/*
* Break up a delimited string into a table of substrings
*
* str - delimited string ( will be modified )
* strp- list of pointers to substrings (this is built by this function), NULL will be placed at end of list
* limit- maximum number of substrings to process
*/
static int finddelim(char *str, char *strp[], int limit)
{
int i,l,inquo;
inquo = 0;
i = 0;
strp[i++] = str;
if (!*str)
{
strp[0] = 0;
return(0);
}
for(l = 0; *str && (l < limit) ; str++)
{
if (*str == QUOTECHR)
{
if (inquo)
{
*str = 0;
inquo = 0;
}
else
{
strp[i - 1] = str + 1;
inquo = 1;
}
}
if ((*str == DELIMCHR) && (!inquo))
{
*str = 0;
l++;
strp[i++] = str + 1;
}
}
strp[i] = 0;
return(i);
}
static unsigned int voter_tvdiff_ms(struct timeval x, struct timeval y)
{
int i;
i = ast_tvdiff_ms(x,y);
if (i < 0) i = INT32_MAX;
return(i);
}
/* return offsetted time */
static long long puckoffset(struct voter_client *client)
{
long long btime,ptime,difftime;
if (!puckit) return 0;
btime = ((long long)client->lastmastergpstime.vtime_sec * 1000000000LL) + client->lastmastergpstime.vtime_nsec;
ptime = ((long long)client->lastgpstime.vtime_sec * 1000000000LL) + client->lastgpstime.vtime_nsec;
difftime = ptime - btime;
return difftime;
}
static void mkpucked(struct voter_client *client,VTIME *dst)
{
long long btime;
btime = ((long long)master_time.vtime_sec * 1000000000LL) + master_time.vtime_nsec;
btime += puckoffset(client);
dst->vtime_nsec = htonl((long)(btime % 1000000000LL));
dst->vtime_sec = htonl((long)(btime / 1000000000LL));
return;
}
/* must be called with voter_lock locked */
static void incr_drainindex(struct voter_pvt *p)
{
struct voter_client *client;
if (p == NULL) return;
for(client = clients; client; client = client->next)
{
if (client->nodenum != p->nodenum) continue;
if (!client->drain40ms)
{
client->drainindex_40ms = client->drainindex;
client->rxseqno_40ms = client->rxseqno;
}
client->drainindex += FRAME_SIZE;
if (client->drainindex >= client->buflen) client->drainindex -= client->buflen;
client->drain40ms = !client->drain40ms;
}
}
static int voter_call(struct ast_channel *ast, char *dest, int timeout)
{
if ((ast->_state != AST_STATE_DOWN) && (ast->_state != AST_STATE_RESERVED)) {
ast_log(LOG_WARNING, "voter_call called on %s, neither down nor reserved\n", ast->name);
return -1;
}
/* When we call, it just works, really, there's no destination... Just
ring the phone and wait for someone to answer */
if (option_debug)
ast_log(LOG_DEBUG, "Calling %s on %s\n", dest, ast->name);
ast_setstate(ast,AST_STATE_UP);
return 0;
}
static int voter_hangup(struct ast_channel *ast)
{
struct voter_pvt *p = ast->tech_pvt,*q;
if (option_debug)
ast_log(LOG_DEBUG, "voter_hangup(%s)\n", ast->name);
if (!ast->tech_pvt) {
ast_log(LOG_WARNING, "Asked to hangup channel not connected\n");
return 0;
}
if (p->dsp) ast_dsp_free(p->dsp);
if (p->adpcmin) ast_translator_free_path(p->adpcmin);
if (p->adpcmout) ast_translator_free_path(p->adpcmout);
if (p->toast) ast_translator_free_path(p->toast);
if (p->toast) ast_translator_free_path(p->toast1);
if (p->fromast) ast_translator_free_path(p->fromast);
if (p->nuin) ast_translator_free_path(p->nuin);
if (p->nuout) ast_translator_free_path(p->nuout);
ast_mutex_lock(&voter_lock);
for(q = pvts; q->next; q = q->next)
{
if (q->next == p) break;
}
if (q->next) q->next = p->next;
if (pvts == p) pvts = p->next;
ast_mutex_unlock(&voter_lock);
ast_free(p);
ast->tech_pvt = NULL;
ast_setstate(ast, AST_STATE_DOWN);
return 0;
}
#ifdef OLD_ASTERISK
static int voter_indicate(struct ast_channel *ast, int cond)
#else
static int voter_indicate(struct ast_channel *ast, int cond, const void *data, size_t datalen)
#endif
{
struct voter_pvt *p = ast->tech_pvt;
switch (cond) {