EDACS; Init Tr; RIGCTL; TCP and WAV in;
This commit is contained in:
parent
eb5cbaad93
commit
ec4ace1514
|
|
@ -0,0 +1,5 @@
|
|||
DEC,Mode,Name of Group,Group Description,
|
||||
100,DE,Example Name,Description,
|
||||
1449,A,Fire Dispatch,Fire,
|
||||
929,A,Titusville Fire Tac,Fire,
|
||||
22033,DE,Law Dispatch,Fire,
|
||||
|
|
|
@ -0,0 +1,2 @@
|
|||
lcn1,lcn2,lcn3,lcn4,lcn5,...lcn26
|
||||
851375000,851800000,855987500,858487500,852312500
|
||||
|
122
include/dsd.h
122
include/dsd.h
|
|
@ -62,6 +62,14 @@
|
|||
|
||||
static volatile int exitflag;
|
||||
|
||||
//group csv import struct
|
||||
typedef struct
|
||||
{
|
||||
unsigned long int groupNumber;
|
||||
char groupMode[8]; //char *?
|
||||
char groupName[50];
|
||||
} groupinfo;
|
||||
|
||||
typedef struct
|
||||
{
|
||||
uint8_t RFChannelType;
|
||||
|
|
@ -247,7 +255,7 @@ typedef struct
|
|||
int p25status;
|
||||
int p25tg;
|
||||
int scoperate;
|
||||
char audio_in_dev[1024];
|
||||
char audio_in_dev[2048]; //increase size for super long directory/file names?
|
||||
int audio_in_fd;
|
||||
SNDFILE *audio_in_file;
|
||||
SF_INFO *audio_in_file_info;
|
||||
|
|
@ -359,6 +367,37 @@ typedef struct
|
|||
FILE *symbolfile;
|
||||
int call_alert;
|
||||
|
||||
//rigctl opt
|
||||
int rigctl_sockfd;
|
||||
int use_rigctl;
|
||||
int rigctlportno;
|
||||
char * rigctlhostname;
|
||||
|
||||
//udp socket for GQRX, SDR++, etc
|
||||
int udp_sockfd;
|
||||
int udp_portno;
|
||||
char * udp_hostname;
|
||||
SNDFILE *udp_file_in;
|
||||
|
||||
//tcp socket for SDR++, etc
|
||||
int tcp_sockfd;
|
||||
int tcp_portno;
|
||||
char * tcp_hostname;
|
||||
SNDFILE *tcp_file_in;
|
||||
|
||||
//wav file sample rate, interpolator and decimator
|
||||
int wav_sample_rate;
|
||||
int wav_interpolator;
|
||||
int wav_decimator;
|
||||
|
||||
int p25_trunk; //enable experimental P25 trunking with RIGCTL (or RTLFM)
|
||||
int p25_is_tuned; //set to 1 if currently on VC, set back to 0 if on CC
|
||||
|
||||
//csv import filenames
|
||||
char group_in_file[1024];
|
||||
char lcn_in_file[1024];
|
||||
//end import filenames
|
||||
|
||||
} dsd_opts;
|
||||
|
||||
typedef struct
|
||||
|
|
@ -612,16 +651,40 @@ typedef struct
|
|||
//dstar header for ncurses
|
||||
unsigned char dstarradioheader[41];
|
||||
|
||||
#ifdef TRACE_DSD
|
||||
char debug_prefix;
|
||||
char debug_prefix_2;
|
||||
unsigned int debug_sample_index;
|
||||
unsigned int debug_sample_left_edge;
|
||||
unsigned int debug_sample_right_edge;
|
||||
FILE* debug_label_file;
|
||||
FILE* debug_label_dibit_file;
|
||||
FILE* debug_label_imbe_file;
|
||||
#endif
|
||||
//dmr trunking stuff
|
||||
int dmr_rest_channel;
|
||||
int dmr_mfid; //just when 'fid' is used as a manufacturer ID and not a feature set id
|
||||
int dmr_vc_lcn;
|
||||
int dmr_vc_lsn;
|
||||
int dmr_tuned_lcn;
|
||||
|
||||
//edacs
|
||||
int ea_mode;
|
||||
int esk_mode;
|
||||
unsigned short esk_mask;
|
||||
unsigned long long int edacs_site_id;
|
||||
int edacs_lcn_count; //running tally of lcn's observed on edacs system
|
||||
int edacs_cc_lcn; //current lcn for the edacs control channel
|
||||
int edacs_vc_lcn; //current lcn for any active vc (not the one we are tuned/tuning to)
|
||||
int edacs_tuned_lcn; //the vc we are currently tuned to...above variable is for updating all in the matrix
|
||||
|
||||
//trunking group and lcn freq list
|
||||
long int trunk_lcn_freq[26]; //max number on an EDACS system, should be enough on DMR too hopefully
|
||||
groupinfo group_array[0x3FF]; //max supported by Cygwin is 3FFF (signed vs unsigned?)
|
||||
unsigned int group_tally; //tally number of groups imported from CSV file for referencing later
|
||||
int lcn_freq_count;
|
||||
int lcn_freq_roll; //number we have 'rolled' to in search of the CC
|
||||
time_t last_cc_sync_time; //use this to start hunting for CC after signal lost
|
||||
|
||||
//new nxdn stuff
|
||||
int nxdn_part_of_frame;
|
||||
int nxdn_ran;
|
||||
int nxdn_sf;
|
||||
bool nxdn_sacch_non_superframe; //flag to indicate whether or not a sacch is a part of a superframe, or an individual piece
|
||||
uint8_t nxdn_sacch_frame_segment[4][18]; //part of frame by 18 bits
|
||||
uint8_t nxdn_sacch_frame_segcrc[4];
|
||||
uint8_t nxdn_alias_block_number;
|
||||
char nxdn_alias_block_segment[4][4][8]; //might be too large? maybe just an 8?
|
||||
|
||||
} dsd_state;
|
||||
|
||||
|
|
@ -662,8 +725,8 @@ typedef struct
|
|||
#define DMR_MS_DATA_SYNC "311131133313133331131113"
|
||||
#define DMR_MS_VOICE_SYNC "133313311131311113313331"
|
||||
|
||||
//Testing NXDN FSW again
|
||||
#define NXDN_FSW "3131331131"
|
||||
#define NXDN_FSW "3131331131"
|
||||
#define INV_NXDN_FSW "1313113313"
|
||||
|
||||
#define DMR_RC_DATA_SYNC "131331111133133133311313"
|
||||
|
||||
|
|
@ -677,6 +740,9 @@ typedef struct
|
|||
#define INV_PROVOICE_EA_SYNC "13313133113113333311313133133311"
|
||||
#define PROVOICE_EA_SYNC "31131311331331111133131311311133"
|
||||
|
||||
#define EDACS_SYNC "313131313131313131313111333133133131313131313131"
|
||||
#define INV_EDACS_SYNC "131313131313131313131333111311311313131313131313"
|
||||
|
||||
#define DPMR_FRAME_SYNC_1 "111333331133131131111313"
|
||||
#define DPMR_FRAME_SYNC_2 "113333131331"
|
||||
#define DPMR_FRAME_SYNC_3 "133131333311"
|
||||
|
|
@ -926,6 +992,36 @@ void process_FACCH_MAC_PDU (dsd_opts * opts, dsd_state * state, int payload[156]
|
|||
//P25 Channel to Frequency
|
||||
long int process_channel_to_freq (dsd_opts * opts, dsd_state * state, int channel);
|
||||
|
||||
//rigctl functions and TCP/UDP functions
|
||||
void error(char *msg);
|
||||
int Connect (char *hostname, int portno);
|
||||
bool Send(int sockfd, char *buf);
|
||||
bool Recv(int sockfd, char *buf);
|
||||
|
||||
//rtl_fm udp tuning function
|
||||
void rtl_udp_tune(dsd_opts * opts, dsd_state * state, long int frequency);
|
||||
|
||||
bool GetCurrentFreq(int sockfd, long int freq);
|
||||
bool SetFreq(int sockfd, long int freq);
|
||||
bool SetModulation(int sockfd, int bandwidth);
|
||||
//commands below unique to GQRX only, not usable on SDR++
|
||||
bool GetSignalLevel(int sockfd, double *dBFS);
|
||||
bool GetSquelchLevel(int sockfd, double *dBFS);
|
||||
bool SetSquelchLevel(int sockfd, double dBFS);
|
||||
bool GetSignalLevelEx(int sockfd, double *dBFS, int n_samp);
|
||||
//end gqrx-scanner
|
||||
|
||||
//UDP socket connection
|
||||
int UDPBind (char *hostname, int portno);
|
||||
|
||||
//Edacs
|
||||
void edacs(dsd_opts * opts, dsd_state * state);
|
||||
unsigned long long int edacs_bch (unsigned long long int message);
|
||||
|
||||
//csv imports
|
||||
int csvGroupImport(dsd_opts * opts, dsd_state * state);
|
||||
int csvLCNImport(dsd_opts * opts, dsd_state * state);
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
|
|
|||
129
src/dsd_audio.c
129
src/dsd_audio.c
|
|
@ -545,31 +545,59 @@ openAudioOutDevice (dsd_opts * opts, int speed)
|
|||
void
|
||||
openAudioInDevice (dsd_opts * opts)
|
||||
{
|
||||
// get info of device/file
|
||||
if(strncmp(opts->audio_in_dev, "-", 1) == 0)
|
||||
{
|
||||
opts->audio_in_type = 1;
|
||||
opts->audio_in_file_info = calloc(1, sizeof(SF_INFO));
|
||||
opts->audio_in_file_info->samplerate=48000;
|
||||
opts->pulse_digi_rate_out = 8000; //set out rate to 8000 for stdin input
|
||||
opts->audio_in_file_info->channels=1;
|
||||
opts->audio_in_file_info->seekable=0;
|
||||
opts->audio_in_file_info->format=SF_FORMAT_RAW|SF_FORMAT_PCM_16|SF_ENDIAN_LITTLE;
|
||||
opts->audio_in_file = sf_open_fd(fileno(stdin), SFM_READ, opts->audio_in_file_info, 0);
|
||||
char * extension;
|
||||
const char ch = '.';
|
||||
extension = strrchr(opts->audio_in_dev, ch); //return extension if this is a .wav or .bin file
|
||||
|
||||
if(opts->audio_in_file == NULL) {
|
||||
fprintf (stderr,"Error, couldn't open stdin with libsndfile: %s\n", sf_strerror(NULL));
|
||||
exit(1); //had this one disabled, re-enabling it now
|
||||
}
|
||||
}
|
||||
//converted to handle any calls to use portaudio
|
||||
else if(strncmp(opts->audio_in_dev, "pa:", 2) == 0)
|
||||
// get info of device/file
|
||||
if (strncmp(opts->audio_in_dev, "-", 1) == 0)
|
||||
{
|
||||
opts->audio_in_type = 0;
|
||||
fprintf (stderr,"Error, Port Audio is not supported by FME!\n");
|
||||
fprintf (stderr,"Using Pulse Audio Input Stream Instead! \n");
|
||||
sprintf (opts->audio_in_dev, "pulse");
|
||||
opts->audio_in_type = 1;
|
||||
opts->audio_in_file_info = calloc(1, sizeof(SF_INFO));
|
||||
opts->audio_in_file_info->samplerate=opts->wav_sample_rate;
|
||||
opts->audio_in_file_info->channels=1;
|
||||
opts->audio_in_file_info->seekable=0;
|
||||
opts->audio_in_file_info->format=SF_FORMAT_RAW|SF_FORMAT_PCM_16|SF_ENDIAN_LITTLE;
|
||||
opts->audio_in_file = sf_open_fd(fileno(stdin), SFM_READ, opts->audio_in_file_info, 0);
|
||||
|
||||
if(opts->audio_in_file == NULL)
|
||||
{
|
||||
fprintf(stderr, "Error, couldn't open stdin with libsndfile: %s\n", sf_strerror(NULL));
|
||||
exit(1);
|
||||
}
|
||||
}
|
||||
else if (strncmp(opts->audio_in_dev, "tcp", 3) == 0)
|
||||
{
|
||||
opts->audio_in_type = 8;
|
||||
opts->audio_in_file_info = calloc(1, sizeof(SF_INFO));
|
||||
opts->audio_in_file_info->samplerate=opts->wav_sample_rate;
|
||||
opts->audio_in_file_info->channels=1;
|
||||
opts->audio_in_file_info->seekable=0;
|
||||
opts->audio_in_file_info->format=SF_FORMAT_RAW|SF_FORMAT_PCM_16|SF_ENDIAN_LITTLE;
|
||||
opts->tcp_file_in = sf_open_fd(opts->tcp_sockfd, SFM_READ, opts->audio_in_file_info, 0);
|
||||
|
||||
if(opts->tcp_file_in == NULL)
|
||||
{
|
||||
fprintf(stderr, "Error, couldn't open TCP with libsndfile: %s\n", sf_strerror(NULL));
|
||||
exit(1);
|
||||
}
|
||||
}
|
||||
else if (strncmp(opts->audio_in_dev, "udp", 3) == 0)
|
||||
{
|
||||
opts->audio_in_type = 6;
|
||||
opts->audio_in_file_info = calloc(1, sizeof(SF_INFO));
|
||||
opts->audio_in_file_info->samplerate=opts->wav_sample_rate;
|
||||
opts->audio_in_file_info->channels=1;
|
||||
opts->audio_in_file_info->seekable=0;
|
||||
opts->audio_in_file_info->format=SF_FORMAT_RAW|SF_FORMAT_PCM_16|SF_ENDIAN_LITTLE;
|
||||
opts->udp_file_in = sf_open_fd(opts->udp_sockfd, SFM_READ, opts->audio_in_file_info, 0);
|
||||
|
||||
if(opts->udp_file_in == NULL)
|
||||
{
|
||||
fprintf(stderr, "Error, couldn't open UDP with libsndfile: %s\n", sf_strerror(NULL));
|
||||
exit(1);
|
||||
}
|
||||
}
|
||||
else if(strncmp(opts->audio_in_dev, "rtl", 3) == 0)
|
||||
{
|
||||
opts->audio_in_type = 3;
|
||||
|
|
@ -578,41 +606,58 @@ openAudioInDevice (dsd_opts * opts)
|
|||
{
|
||||
opts->audio_in_type = 0;
|
||||
}
|
||||
//convert from opening wav files to OP25 symbol capture files since opening wav files is busted
|
||||
else if (strncmp(extension, ".bin", 3) == 0)
|
||||
{
|
||||
struct stat stat_buf;
|
||||
if (stat(opts->audio_in_dev, &stat_buf) != 0)
|
||||
{
|
||||
fprintf (stderr,"Error, couldn't open bin file %s\n", opts->audio_in_dev);
|
||||
exit(1);
|
||||
}
|
||||
if (S_ISREG(stat_buf.st_mode))
|
||||
{
|
||||
opts->symbolfile = fopen(opts->audio_in_dev, "r");
|
||||
opts->audio_in_type = 4; //symbol capture bin files
|
||||
}
|
||||
else
|
||||
{
|
||||
opts->audio_in_type = 0;
|
||||
}
|
||||
}
|
||||
//open as wav file as last resort, wav files subseptible to sample rate issues if not 48000
|
||||
else
|
||||
{
|
||||
struct stat stat_buf;
|
||||
if (stat(opts->audio_in_dev, &stat_buf) != 0)
|
||||
{
|
||||
fprintf (stderr,"Error, couldn't open %s\n", opts->audio_in_dev);
|
||||
fprintf (stderr,"Error, couldn't open wav file %s\n", opts->audio_in_dev);
|
||||
exit(1);
|
||||
}
|
||||
if (S_ISREG(stat_buf.st_mode))
|
||||
{
|
||||
// is this a regular file? then process with libsndfile.
|
||||
//opts->pulse_digi_rate_out = 8000; //this for wav files input?
|
||||
// opts->audio_in_type = 1;
|
||||
// opts->audio_in_file_info = calloc(1, sizeof(SF_INFO));
|
||||
// opts->audio_in_file_info->channels = 1;
|
||||
// opts->audio_in_file = sf_open(opts->audio_in_dev, SFM_READ, opts->audio_in_file_info);
|
||||
opts->audio_in_type = 2; //two now, seperating STDIN and wav files
|
||||
opts->audio_in_file_info = calloc(1, sizeof(SF_INFO));
|
||||
opts->audio_in_file_info->samplerate=opts->wav_sample_rate;
|
||||
opts->audio_in_file_info->channels=1;
|
||||
opts->audio_in_file_info->channels = 1;
|
||||
opts->audio_in_file_info->seekable=0;
|
||||
opts->audio_in_file_info->format=SF_FORMAT_RAW|SF_FORMAT_PCM_16|SF_ENDIAN_LITTLE;
|
||||
//
|
||||
// if(opts->audio_in_file == NULL)
|
||||
// {
|
||||
// fprintf (stderr,"Error, couldn't open file %s\n", opts->audio_in_dev);
|
||||
// exit(1);
|
||||
// }
|
||||
opts->audio_in_file = sf_open(opts->audio_in_dev, SFM_READ, opts->audio_in_file_info);
|
||||
|
||||
if(opts->audio_in_file == NULL)
|
||||
{
|
||||
fprintf(stderr, "Error, couldn't open wav file %s\n", opts->audio_in_dev);
|
||||
exit(1);
|
||||
}
|
||||
|
||||
opts->symbolfile = fopen(opts->audio_in_dev, "r");
|
||||
opts->audio_in_type = 4; //symbol capture bin files
|
||||
}
|
||||
else
|
||||
//open pulse audio if no bin or wav
|
||||
else
|
||||
{
|
||||
// this is a device, use old handling, pulse audio now
|
||||
opts->audio_in_type = 0;
|
||||
|
||||
opts->audio_in_type = 0; //not sure if this works or needs to openPulse here
|
||||
}
|
||||
}
|
||||
|
||||
if (opts->split == 1)
|
||||
{
|
||||
fprintf (stderr,"Audio In Device: %s\n", opts->audio_in_dev);
|
||||
|
|
|
|||
|
|
@ -221,12 +221,13 @@ static int digitize (dsd_opts* opts, dsd_state* state, int symbol)
|
|||
//int digitize (dsd_opts* opts, dsd_state* state, int symbol)
|
||||
{
|
||||
// determine dibit state
|
||||
if ((state->synctype == 6) || (state->synctype == 14)|| (state->synctype == 18))
|
||||
if ((state->synctype == 6) || (state->synctype == 14)|| (state->synctype == 18) || (state->synctype == 37))
|
||||
|
||||
{
|
||||
// 6 +D-STAR
|
||||
// 14 +ProVoice
|
||||
// 18 +D-STAR_HD
|
||||
// 37 +EDACS
|
||||
|
||||
if (symbol > state->center)
|
||||
{
|
||||
|
|
@ -241,11 +242,12 @@ static int digitize (dsd_opts* opts, dsd_state* state, int symbol)
|
|||
return (1); // +3
|
||||
}
|
||||
}
|
||||
else if ((state->synctype == 7) || (state->synctype == 15)|| (state->synctype == 19))
|
||||
else if ((state->synctype == 7) || (state->synctype == 15)|| (state->synctype == 19) || (state->synctype == 38))
|
||||
{
|
||||
// 7 -D-STAR
|
||||
// 15 -ProVoice
|
||||
// 19 -D-STAR_HD
|
||||
// 38 -EDACS
|
||||
|
||||
if (symbol > state->center)
|
||||
{
|
||||
|
|
|
|||
|
|
@ -287,9 +287,9 @@ processFrame (dsd_opts * opts, dsd_state * state)
|
|||
}
|
||||
else if ((state->synctype == 14) || (state->synctype == 15))
|
||||
{
|
||||
state->nac = 0;
|
||||
state->lastsrc = 0;
|
||||
state->lasttg = 0;
|
||||
//state->nac = 0;
|
||||
//state->lastsrc = 0;
|
||||
//state->lasttg = 0;
|
||||
if (opts->errorbars == 1)
|
||||
{
|
||||
if (opts->verbose > 0)
|
||||
|
|
@ -306,6 +306,12 @@ processFrame (dsd_opts * opts, dsd_state * state)
|
|||
processProVoice (opts, state);
|
||||
return;
|
||||
}
|
||||
//edacs
|
||||
else if ((state->synctype == 37) || (state->synctype == 38))
|
||||
{
|
||||
edacs (opts, state);
|
||||
return;
|
||||
}
|
||||
//ysf
|
||||
else if ((state->synctype == 30) || (state->synctype == 31))
|
||||
{
|
||||
|
|
|
|||
|
|
@ -146,8 +146,44 @@ getFrameSync (dsd_opts * opts, dsd_state * state)
|
|||
* 34 = DMR RC Data
|
||||
* 35 = +P25 P2
|
||||
* 36 = -P25 P2
|
||||
* 37 = +EDACS
|
||||
* 38 = -EDACS
|
||||
*/
|
||||
|
||||
//start control channel hunting if using trunking, time needs updating on each successful sync
|
||||
//will need to assign frequencies to a CC array for P25 since that isn't imported from CSV
|
||||
if (opts->p25_is_tuned == 0 && opts->p25_trunk == 1 && time(NULL) - state->last_cc_sync_time > 3)
|
||||
{
|
||||
//start going through the lcn/frequencies CC/signal hunting
|
||||
fprintf (stderr, "Control Channel Signal Lost. Searching for Control Channel.\n");
|
||||
//make sure our current roll value doesn't exceed value of frequencies imported
|
||||
if (state->lcn_freq_roll > state->lcn_freq_count)
|
||||
{
|
||||
state->lcn_freq_roll = 0; //reset to zero
|
||||
}
|
||||
//check that we have a non zero value first, then tune next frequency
|
||||
if (state->trunk_lcn_freq[state->lcn_freq_roll] != 0)
|
||||
{
|
||||
//do condition here, in future, will allow us to use tuning methods as well, or rtl_udp as well
|
||||
if (opts->use_rigctl == 1)
|
||||
{
|
||||
SetModulation(opts->rigctl_sockfd, 12500); //may not use this here, not sure yet
|
||||
SetFreq(opts->rigctl_sockfd, state->trunk_lcn_freq[state->lcn_freq_roll]);
|
||||
}
|
||||
|
||||
if (opts->audio_in_type == 3)
|
||||
{
|
||||
rtl_udp_tune (opts, state, state->trunk_lcn_freq[state->lcn_freq_roll]);
|
||||
}
|
||||
|
||||
fprintf (stderr, "Tuning to Control Channel Frequency: %.06lf MHz\n",
|
||||
(double)state->trunk_lcn_freq[state->lcn_freq_roll]/1000000);
|
||||
|
||||
}
|
||||
state->lcn_freq_roll++;
|
||||
state->last_cc_sync_time = time(NULL); //set again to give another x seconds
|
||||
}
|
||||
|
||||
|
||||
int i, j, t, o, dibit, sync, symbol, synctest_pos, lastt;
|
||||
char synctest[25];
|
||||
|
|
@ -156,6 +192,7 @@ getFrameSync (dsd_opts * opts, dsd_state * state)
|
|||
char synctest18[19];
|
||||
char synctest32[33];
|
||||
char synctest20[21]; //YSF
|
||||
char synctest48[49]; //EDACS
|
||||
char modulation[8];
|
||||
char *synctest_p;
|
||||
char synctest_buf[10240];
|
||||
|
|
@ -177,6 +214,7 @@ getFrameSync (dsd_opts * opts, dsd_state * state)
|
|||
synctest18[18] = 0;
|
||||
synctest32[32] = 0;
|
||||
synctest20[20] = 0;
|
||||
synctest48[48] = 0;
|
||||
synctest_pos = 0;
|
||||
synctest_p = synctest_buf + 10;
|
||||
sync = 0;
|
||||
|
|
@ -326,7 +364,7 @@ getFrameSync (dsd_opts * opts, dsd_state * state)
|
|||
// end digitize and dmr buffer testing
|
||||
|
||||
*synctest_p = dibit;
|
||||
if (t >= 18)
|
||||
if (t >= 10) //t >= 18
|
||||
{
|
||||
for (i = 0; i < 24; i++)
|
||||
{
|
||||
|
|
@ -475,6 +513,7 @@ getFrameSync (dsd_opts * opts, dsd_state * state)
|
|||
printFrameSync (opts, state, "+P25p1 ", synctest_pos + 1, modulation);
|
||||
}
|
||||
state->lastsynctype = 0;
|
||||
state->last_cc_sync_time = time(NULL);
|
||||
return (0);
|
||||
}
|
||||
if (strcmp (synctest, INV_P25P1_SYNC) == 0)
|
||||
|
|
@ -491,6 +530,7 @@ getFrameSync (dsd_opts * opts, dsd_state * state)
|
|||
printFrameSync (opts, state, "-P25p1 ", synctest_pos + 1, modulation);
|
||||
}
|
||||
state->lastsynctype = 1;
|
||||
state->last_cc_sync_time = time(NULL);
|
||||
return (1);
|
||||
}
|
||||
}
|
||||
|
|
@ -652,6 +692,7 @@ getFrameSync (dsd_opts * opts, dsd_state * state)
|
|||
{
|
||||
pa_simple_flush(opts->pulse_raw_dev_out, NULL);
|
||||
}
|
||||
state->last_cc_sync_time = time(NULL);
|
||||
return (35); //35
|
||||
}
|
||||
}
|
||||
|
|
@ -695,6 +736,7 @@ getFrameSync (dsd_opts * opts, dsd_state * state)
|
|||
{
|
||||
pa_simple_flush(opts->pulse_raw_dev_out, NULL);
|
||||
}
|
||||
state->last_cc_sync_time = time(NULL);
|
||||
return (36); //36
|
||||
}
|
||||
}
|
||||
|
|
@ -1149,50 +1191,64 @@ getFrameSync (dsd_opts * opts, dsd_state * state)
|
|||
} //End if (opts->frame_dmr == 1)
|
||||
|
||||
//end DMR Sync
|
||||
|
||||
//ProVoice and EDACS sync
|
||||
if (opts->frame_provoice == 1)
|
||||
{
|
||||
strncpy (synctest32, (synctest_p - 31), 32);
|
||||
strncpy (synctest48, (synctest_p - 47), 48);
|
||||
if ((strcmp (synctest32, PROVOICE_SYNC) == 0) || (strcmp (synctest32, PROVOICE_EA_SYNC) == 0))
|
||||
{
|
||||
strncpy (synctest32, (synctest_p - 31), 32);
|
||||
if ((strcmp (synctest32, PROVOICE_SYNC) == 0) || (strcmp (synctest32, PROVOICE_EA_SYNC) == 0))
|
||||
{
|
||||
now = time(NULL);
|
||||
state->carrier = 1;
|
||||
state->offset = synctest_pos;
|
||||
state->max = ((state->max) + lmax) / 2;
|
||||
state->min = ((state->min) + lmin) / 2;
|
||||
sprintf (state->ftype, "ProVoice ");
|
||||
if (opts->errorbars == 1)
|
||||
//if (opts->errorbars == 1 && (time(NULL) - now) > 2 )
|
||||
{
|
||||
printFrameSync (opts, state, "-ProVoice ", synctest_pos + 1, modulation);
|
||||
}
|
||||
state->lastsynctype = 14;
|
||||
if ( opts->monitor_input_audio == 1)
|
||||
{
|
||||
state->last_cc_sync_time = time(NULL);
|
||||
state->carrier = 1;
|
||||
state->offset = synctest_pos;
|
||||
state->max = ((state->max) + lmax) / 2;
|
||||
state->min = ((state->min) + lmin) / 2;
|
||||
sprintf (state->ftype, "ProVoice ");
|
||||
if (opts->errorbars == 1)
|
||||
printFrameSync (opts, state, "+PV ", synctest_pos + 1, modulation);
|
||||
state->lastsynctype = 14;
|
||||
state->last_cc_sync_time = time(NULL);
|
||||
return (14);
|
||||
}
|
||||
else if ((strcmp (synctest32, INV_PROVOICE_SYNC) == 0) || (strcmp (synctest32, INV_PROVOICE_EA_SYNC) == 0))
|
||||
{
|
||||
state->last_cc_sync_time = time(NULL);
|
||||
state->carrier = 1;
|
||||
state->offset = synctest_pos;
|
||||
state->max = ((state->max) + lmax) / 2;
|
||||
state->min = ((state->min) + lmin) / 2;
|
||||
sprintf (state->ftype, "ProVoice ");
|
||||
printFrameSync (opts, state, "-PV ", synctest_pos + 1, modulation);
|
||||
state->lastsynctype = 15;
|
||||
state->last_cc_sync_time = time(NULL);
|
||||
return (15);
|
||||
}
|
||||
|
||||
pa_simple_flush(opts->pulse_raw_dev_out, NULL);
|
||||
}
|
||||
return (14);
|
||||
}
|
||||
else if ((strcmp (synctest32, INV_PROVOICE_SYNC) == 0) || (strcmp (synctest32, INV_PROVOICE_EA_SYNC) == 0))
|
||||
{
|
||||
now = time(NULL);
|
||||
state->carrier = 1;
|
||||
state->offset = synctest_pos;
|
||||
state->max = ((state->max) + lmax) / 2;
|
||||
state->min = ((state->min) + lmin) / 2;
|
||||
sprintf (state->ftype, "ProVoice ");
|
||||
if (opts->errorbars == 1)
|
||||
{
|
||||
printFrameSync (opts, state, "-ProVoice ", synctest_pos + 1, modulation);
|
||||
}
|
||||
state->lastsynctype = 15;
|
||||
if ( opts->monitor_input_audio == 1)
|
||||
{
|
||||
|
||||
pa_simple_flush(opts->pulse_raw_dev_out, NULL);
|
||||
}
|
||||
return (15);
|
||||
}
|
||||
else if ( strcmp (synctest48, EDACS_SYNC) == 0)
|
||||
{
|
||||
state->last_cc_sync_time = time(NULL);
|
||||
state->carrier = 1;
|
||||
state->offset = synctest_pos;
|
||||
state->max = ((state->max) + lmax) / 2;
|
||||
state->min = ((state->min) + lmin) / 2;
|
||||
printFrameSync (opts, state, "-EDACS", synctest_pos + 1, modulation);
|
||||
state->lastsynctype = 38;
|
||||
state->last_cc_sync_time = time(NULL);
|
||||
return (38);
|
||||
}
|
||||
else if ( strcmp (synctest48, INV_EDACS_SYNC) == 0)
|
||||
{
|
||||
state->last_cc_sync_time = time(NULL);
|
||||
state->carrier = 1;
|
||||
state->offset = synctest_pos;
|
||||
state->max = ((state->max) + lmax) / 2;
|
||||
state->min = ((state->min) + lmin) / 2;
|
||||
printFrameSync (opts, state, "+EDACS", synctest_pos + 1, modulation);
|
||||
state->lastsynctype = 37;
|
||||
state->last_cc_sync_time = time(NULL);
|
||||
return (37);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -0,0 +1,96 @@
|
|||
|
||||
|
||||
#include "dsd.h"
|
||||
#define BSIZE 999
|
||||
|
||||
int csvGroupImport(dsd_opts * opts, dsd_state * state)
|
||||
{
|
||||
char filename[1024] = "filename.csv";
|
||||
sprintf (filename, "%s", opts->group_in_file);
|
||||
//filename[1023] = '\0'; //necessary?
|
||||
char buffer[BSIZE];
|
||||
FILE * fp;
|
||||
fp = fopen(filename, "r");
|
||||
if (fp == NULL) {
|
||||
printf("Unable to open file '%s'\n", filename);
|
||||
exit(1);
|
||||
}
|
||||
int row_count = 0;
|
||||
int field_count = 0;
|
||||
long int group_number = 0; //local group number for array index value
|
||||
int i = 0;
|
||||
while (fgets(buffer, BSIZE, fp)) {
|
||||
field_count = 0;
|
||||
row_count++;
|
||||
if (row_count == 1)
|
||||
continue; //don't want labels
|
||||
char * field = strtok(buffer, ","); //seperate by comma
|
||||
while (field) {
|
||||
|
||||
if (field_count == 0)
|
||||
{
|
||||
//group_number = atol(field);
|
||||
state->group_array[i].groupNumber = atol(field);
|
||||
fprintf (stderr, "%ld, ", state->group_array[i].groupNumber);
|
||||
}
|
||||
if (field_count == 1)
|
||||
{
|
||||
strcpy(state->group_array[i].groupMode, field);
|
||||
fprintf (stderr, "%s, ", state->group_array[i].groupMode);
|
||||
}
|
||||
if (field_count == 2)
|
||||
{
|
||||
strcpy(state->group_array[i].groupName, field);
|
||||
fprintf (stderr, "%s ", state->group_array[i].groupName);
|
||||
}
|
||||
|
||||
field = strtok(NULL, ",");
|
||||
field_count++;
|
||||
}
|
||||
fprintf (stderr, "\n");
|
||||
i++;
|
||||
state->group_tally++;
|
||||
}
|
||||
fclose(fp);
|
||||
return 0;
|
||||
}
|
||||
|
||||
int csvLCNImport(dsd_opts * opts, dsd_state * state) //LCN/LSN import for EDACS/DMR/NXDN?
|
||||
{
|
||||
char filename[1024] = "filename.csv";
|
||||
sprintf (filename, "%s", opts->lcn_in_file);
|
||||
//filename[1023] = '\0'; //necessary?
|
||||
char buffer[BSIZE];
|
||||
FILE * fp;
|
||||
fp = fopen(filename, "r");
|
||||
if (fp == NULL) {
|
||||
printf("Unable to open file '%s'\n", filename);
|
||||
//have this return -1 and handle it inside of main
|
||||
exit(1);
|
||||
}
|
||||
int row_count = 0;
|
||||
int field_count = 0;
|
||||
|
||||
while (fgets(buffer, BSIZE, fp)) {
|
||||
field_count = 0;
|
||||
row_count++;
|
||||
if (row_count == 1)
|
||||
continue; //don't want labels
|
||||
char * field = strtok(buffer, ","); //seperate by comma
|
||||
while (field) {
|
||||
|
||||
state->trunk_lcn_freq[field_count] = atol (field);
|
||||
state->lcn_freq_count++; //keep tally of number of Frequencies imported
|
||||
fprintf (stderr, "LCN [%d] [%ld]", field_count+1, state->trunk_lcn_freq[field_count]);
|
||||
fprintf (stderr, "\n");
|
||||
|
||||
|
||||
field = strtok(NULL, ",");
|
||||
field_count++;
|
||||
}
|
||||
fprintf (stderr, "LCN Count %d\n", state->lcn_freq_count);
|
||||
|
||||
}
|
||||
fclose(fp);
|
||||
return 0;
|
||||
}
|
||||
214
src/dsd_main.c
214
src/dsd_main.c
|
|
@ -76,6 +76,43 @@ char * pEnd; //bugfix
|
|||
void
|
||||
noCarrier (dsd_opts * opts, dsd_state * state)
|
||||
{
|
||||
|
||||
//tune back to last knwon CC when using trunking
|
||||
if (opts->p25_trunk == 1 && opts->p25_is_tuned == 1)
|
||||
{
|
||||
if (state->p25_cc_freq != 0) //still need a con+ way to set this accurately, or atleast guess, maybe shim p25_cc_freq to lcn index 0?
|
||||
{
|
||||
//maybe I should make a seperate tuning function that can handle multiple tuning types
|
||||
if (opts->use_rigctl == 1) //rigctl tuning
|
||||
{
|
||||
SetModulation(opts->rigctl_sockfd, 12500); //could use the bw field on iden_up for P25 to determine a good bw, but honestly it really depends on signal stregth
|
||||
SetFreq(opts->rigctl_sockfd, state->p25_cc_freq);
|
||||
}
|
||||
|
||||
else if (opts->audio_in_type == 3) //rtl_fm tuning
|
||||
{
|
||||
//UDP command to tune the RTL dongle
|
||||
rtl_udp_tune(opts, state, state->p25_cc_freq);
|
||||
}
|
||||
|
||||
opts->p25_is_tuned = 0;
|
||||
state->edacs_tuned_lcn = -1;
|
||||
}
|
||||
//only for EDACS right now, disable this later on or work around
|
||||
if (opts->wav_out_file != NULL)
|
||||
{
|
||||
closeWavOutFile(opts, state);
|
||||
}
|
||||
state->last_cc_sync_time = time(NULL);
|
||||
//test to revert back to 10/4 P1 QPSK
|
||||
if (opts->mod_qpsk == 1)
|
||||
{
|
||||
state->samplesPerSymbol = 10;
|
||||
state->symbolCenter = 4;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
state->dibit_buf_p = state->dibit_buf + 200;
|
||||
memset (state->dibit_buf, 0, sizeof (int) * 200);
|
||||
//dmr buffer
|
||||
|
|
@ -212,6 +249,16 @@ noCarrier (dsd_opts * opts, dsd_state * state)
|
|||
state->p25_vc_freq[0] = 0;
|
||||
state->p25_vc_freq[1] = 0;
|
||||
|
||||
//new nxdn stuff
|
||||
state->nxdn_part_of_frame = 0;
|
||||
state->nxdn_ran = 0;
|
||||
state->nxdn_sf = 0;
|
||||
memset (state->nxdn_sacch_frame_segcrc, 1, sizeof(state->nxdn_sacch_frame_segcrc)); //init on 1, bad CRC all
|
||||
state->nxdn_sacch_non_superframe = TRUE;
|
||||
memset (state->nxdn_sacch_frame_segment, 0, sizeof(state->nxdn_sacch_frame_segment));
|
||||
state->nxdn_alias_block_number = 0;
|
||||
memset (state->nxdn_alias_block_segment, 0, sizeof(state->nxdn_alias_block_segment));
|
||||
|
||||
}
|
||||
|
||||
void
|
||||
|
|
@ -304,6 +351,10 @@ initOpts (dsd_opts * opts)
|
|||
opts->pulse_digi_in_channels = 1; //2
|
||||
opts->pulse_digi_out_channels = 2; //new default for XDMA
|
||||
|
||||
opts->wav_sample_rate = 48000; //default value (DSDPlus uses 96000 on raw signal wav files)
|
||||
opts->wav_interpolator = 1; //default factor of 1 on 48000; 2 on 96000; sample rate / decimator
|
||||
opts->wav_decimator = 48000; //maybe for future use?
|
||||
|
||||
sprintf (opts->output_name, "XDMA");
|
||||
opts->pulse_flush = 1; //set 0 to flush, 1 for flushed
|
||||
opts->use_ncurses_terminal = 0;
|
||||
|
|
@ -329,6 +380,25 @@ initOpts (dsd_opts * opts)
|
|||
|
||||
opts->call_alert = 0; //call alert beeper for ncurses
|
||||
|
||||
//rigctl options
|
||||
opts->use_rigctl = 0;
|
||||
opts->rigctl_sockfd = 0;
|
||||
opts->rigctlportno = 7356; //TCP Port Number; GQRX - 7356; SDR++ - 4532
|
||||
opts->rigctlhostname = "localhost";
|
||||
|
||||
//udp input options
|
||||
opts->udp_sockfd = 0;
|
||||
opts->udp_portno = 7355; //default favored by GQRX and SDR++
|
||||
opts->udp_hostname = "localhost";
|
||||
|
||||
//tcp input options
|
||||
opts->tcp_sockfd = 0;
|
||||
opts->tcp_portno = 7355; //default favored by SDR++
|
||||
opts->tcp_hostname = "localhost";
|
||||
|
||||
opts->p25_trunk = 0; //0 disabled, 1 is enabled
|
||||
opts->p25_is_tuned = 0; //set to 1 if currently on VC, set back to 0 if on CC
|
||||
|
||||
}
|
||||
|
||||
void
|
||||
|
|
@ -600,17 +670,47 @@ initState (dsd_state * state)
|
|||
state->p25_chan_spac[i] = 0;
|
||||
state->p25_base_freq[i] = 0;
|
||||
}
|
||||
|
||||
//values displayed in ncurses terminal
|
||||
state->p25_cc_freq = 0;
|
||||
state->p25_vc_freq[0] = 0;
|
||||
state->p25_vc_freq[1] = 0;
|
||||
|
||||
#ifdef TRACE_DSD
|
||||
state->debug_sample_index = 0;
|
||||
state->debug_label_file = NULL;
|
||||
state->debug_label_dibit_file = NULL;
|
||||
state->debug_label_imbe_file = NULL;
|
||||
#endif
|
||||
//edacs
|
||||
state->ea_mode = -1; //init on -1, 0 is standard, 1 is ea
|
||||
state->esk_mode = -1; //same as above, but with esk or not
|
||||
state->esk_mask = 0x0; //toggles from 0x0 to 0xA0 if esk mode enabled
|
||||
state->edacs_site_id = 0;
|
||||
state->edacs_lcn_count = 0;
|
||||
state->edacs_cc_lcn = 0;
|
||||
state->edacs_vc_lcn = 0;
|
||||
state->edacs_tuned_lcn = -1;
|
||||
|
||||
//trunking
|
||||
memset (state->trunk_lcn_freq, 0, sizeof(state->trunk_lcn_freq));
|
||||
state->group_tally = 0;
|
||||
state->lcn_freq_count = 0; //number of frequncies imported from LCN
|
||||
state->lcn_freq_roll = 0; //needs reset if sync is found?
|
||||
state->last_cc_sync_time = time(NULL);
|
||||
|
||||
//dmr trunking/ncurses stuff
|
||||
state->dmr_rest_channel = -1; //init on -1
|
||||
state->dmr_mfid = -1; //
|
||||
state->dmr_tuned_lcn = -1; //logical slot, lcn * ts?
|
||||
state->dmr_vc_lcn = -1; //
|
||||
state->dmr_vc_lsn = -1;
|
||||
|
||||
//new nxdn stuff
|
||||
state->nxdn_part_of_frame = 0;
|
||||
state->nxdn_ran = 0;
|
||||
state->nxdn_sf = 0;
|
||||
memset (state->nxdn_sacch_frame_segcrc, 1, sizeof(state->nxdn_sacch_frame_segcrc)); //init on 1, bad CRC all
|
||||
state->nxdn_sacch_non_superframe = TRUE;
|
||||
memset (state->nxdn_sacch_frame_segment, 0, sizeof(state->nxdn_sacch_frame_segment));
|
||||
state->nxdn_alias_block_number = 0;
|
||||
memset (state->nxdn_alias_block_segment, 0, sizeof(state->nxdn_alias_block_segment));
|
||||
|
||||
|
||||
|
||||
initialize_p25_heuristics(&state->p25_heuristics);
|
||||
}
|
||||
|
|
@ -641,6 +741,11 @@ usage ()
|
|||
printf ("Input/Output options:\n");
|
||||
printf (" -i <device> Audio input device (default is pulse audio)\n");
|
||||
printf (" - for piped stdin, rtl for rtl device\n");
|
||||
printf (" tcp for tcp client SDR++/GNURadio Companion/Other (Port 7355)\n");
|
||||
printf (" filename.bin for OP25/FME capture bin files\n");
|
||||
printf (" filename.wav for 48K/1 wav files (SDR++, GQRX)\n");
|
||||
printf (" filename.wav -s 96000 for 96K/1 wav files (DSDPlus)\n");
|
||||
printf (" (Use single quotes '/directory/audio file.wav' when directories/spaces are present)\n");
|
||||
printf (" filename.bin for OP25/FME capture bin files\n");
|
||||
printf (" -o <device> Audio output device (default is pulse audio)(null for no audio output)\n");
|
||||
printf (" -d <dir> Create mbe data files, use this directory\n");
|
||||
|
|
@ -680,7 +785,7 @@ usage ()
|
|||
printf (" -fx Decode only X2-TDMA\n");
|
||||
printf (" -fi Decode only NXDN48* (6.25 kHz) / IDAS*\n");
|
||||
printf (" -fn Decode only NXDN96* (12.5 kHz)\n");
|
||||
printf (" -fp Decode only ProVoice*\n");
|
||||
printf (" -fp Decode only EDACS/ProVoice*\n");
|
||||
printf (" -fm Decode only dPMR*\n");
|
||||
printf (" -l Disable DMR and NXDN input filtering\n");
|
||||
printf (" -pu Unmute Encrypted P25\n");
|
||||
|
|
@ -725,6 +830,15 @@ usage ()
|
|||
printf (" \n");
|
||||
printf (" -4 Force Privacy Key over FID and SVC bits \n");
|
||||
printf ("\n");
|
||||
printf (" Experimental Functions and Features---------------------------------------------------\n");
|
||||
printf (" -1 <file> Import LCN Frequencies from csv file (numeral 'one') \n");
|
||||
printf (" (See lcn.csv for example)\n");
|
||||
printf (" -2 <file> Import Group List Allow/Block and Label from csv file (numeral 'two')\n");
|
||||
printf (" (See group.csv for example)\n");
|
||||
printf (" -3 Enable Experimental Trunking Features (P25/EDACS for now) with RIGCTL/TCP or RTL Input\n");
|
||||
printf (" -5 <udp p> Enable RIGCTL/TCP; Set UDP Port for RIGCTL. (4532 on SDR++)\n");
|
||||
//printf (" (Currently only available on UDP port 4532)\n");
|
||||
printf ("\n");
|
||||
exit (0);
|
||||
}
|
||||
|
||||
|
|
@ -856,15 +970,6 @@ cleanupAndExit (dsd_opts * opts, dsd_state * state)
|
|||
closeMbeOutFile (opts, state);
|
||||
}
|
||||
|
||||
#ifdef USE_RTLSDR
|
||||
if(opts->audio_in_type == 3)
|
||||
{
|
||||
// TODO: cleanup demod threads
|
||||
//temp disable to see if this corrects issues with closing
|
||||
//cleanup_rtlsdr_stream();
|
||||
}
|
||||
#endif
|
||||
|
||||
fprintf (stderr,"\n");
|
||||
fprintf (stderr,"Total audio errors: %i\n", state->debug_audio_errors);
|
||||
fprintf (stderr,"Total header errors: %i\n", state->debug_header_errors);
|
||||
|
|
@ -963,7 +1068,7 @@ main (int argc, char **argv)
|
|||
exitflag = 0;
|
||||
signal (SIGINT, sigfun);
|
||||
|
||||
while ((c = getopt (argc, argv, "haep:P:qstv:z:i:o:d:c:g:nw:B:C:R:f:m:u:x:A:S:M:G:D:L:V:U:Y:K:H:X:NQWrlZTF4")) != -1)
|
||||
while ((c = getopt (argc, argv, "haep:P:qs:tv:z:i:o:d:c:g:nw:B:C:R:f:m:u:x:A:S:M:G:D:L:V:U:Y:K:H:X:NQWrlZTF1:2:345:")) != -1)
|
||||
{
|
||||
opterr = 0;
|
||||
switch (c)
|
||||
|
|
@ -975,6 +1080,30 @@ main (int argc, char **argv)
|
|||
//printPortAudioDevices();
|
||||
//exit(0);
|
||||
opts.call_alert = 1;
|
||||
//placeholder until letters get re-arranged (or opt_long switched in)
|
||||
case '1': //LCN/Frequency CSV
|
||||
strncpy(opts.lcn_in_file, optarg, 1023);
|
||||
opts.lcn_in_file[1023] = '\0';
|
||||
csvLCNImport (&opts, &state);
|
||||
break;
|
||||
//placeholder until letters get re-arranged
|
||||
case '2': //Group Label CSV
|
||||
strncpy(opts.group_in_file, optarg, 1023);
|
||||
opts.group_in_file[1023] = '\0';
|
||||
csvGroupImport(&opts, &state);
|
||||
break;
|
||||
//placeholder until letters get re-arranged
|
||||
case '3':
|
||||
opts.p25_trunk = 1;
|
||||
break;
|
||||
//placeholder until letters get re-arranged
|
||||
case '5': //RIGCTL UDP port
|
||||
sscanf (optarg, "%d", &opts.rigctlportno);
|
||||
if (opts.rigctlportno != 0)
|
||||
{
|
||||
opts.use_rigctl = 1;
|
||||
}
|
||||
break;
|
||||
case 'e':
|
||||
opts.errorbars = 1;
|
||||
opts.datascope = 0;
|
||||
|
|
@ -1011,13 +1140,18 @@ main (int argc, char **argv)
|
|||
opts.verbose = 0;
|
||||
break;
|
||||
case 's':
|
||||
opts.errorbars = 0;
|
||||
opts.p25enc = 0;
|
||||
opts.p25lc = 0;
|
||||
opts.p25status = 0;
|
||||
opts.p25tg = 0;
|
||||
opts.datascope = 1;
|
||||
opts.symboltiming = 0;
|
||||
// opts.errorbars = 0;
|
||||
// opts.p25enc = 0;
|
||||
// opts.p25lc = 0;
|
||||
// opts.p25status = 0;
|
||||
// opts.p25tg = 0;
|
||||
// opts.datascope = 1;
|
||||
// opts.symboltiming = 0;
|
||||
sscanf (optarg, "%d", &opts.wav_sample_rate);
|
||||
opts.wav_interpolator = opts.wav_sample_rate / opts.wav_decimator;
|
||||
state.samplesPerSymbol = state.samplesPerSymbol * opts.wav_interpolator;
|
||||
state.symbolCenter = state.symbolCenter * opts.wav_interpolator;
|
||||
//fprintf (stderr, "Are we getting this far?\n");
|
||||
break;
|
||||
case 't':
|
||||
opts.symboltiming = 1;
|
||||
|
|
@ -1193,9 +1327,8 @@ main (int argc, char **argv)
|
|||
fprintf (stderr,"Setting datascope frame rate to %i frame per second.\n", opts.scoperate);
|
||||
break;
|
||||
case 'i':
|
||||
strncpy(opts.audio_in_dev, optarg, 1023);
|
||||
opts.audio_in_dev[1023] = '\0';
|
||||
//fprintf (stderr,"audio_in_dev = %s\n", opts.audio_in_dev);
|
||||
strncpy(opts.audio_in_dev, optarg, 2047);
|
||||
opts.audio_in_dev[2047] = '\0';
|
||||
break;
|
||||
case 'o':
|
||||
strncpy(opts.audio_out_dev, optarg, 1023);
|
||||
|
|
@ -1338,7 +1471,7 @@ main (int argc, char **argv)
|
|||
opts.pulse_digi_out_channels = 1;
|
||||
opts.dmr_stereo = 0;
|
||||
state.dmr_stereo = 0;
|
||||
sprintf (opts.output_name, "ProVoice");
|
||||
sprintf (opts.output_name, "EDACS/PV");
|
||||
fprintf (stderr,"Setting symbol rate to 9600 / second\n");
|
||||
fprintf (stderr,"Decoding only ProVoice frames.\n");
|
||||
}
|
||||
|
|
@ -1693,6 +1826,31 @@ main (int argc, char **argv)
|
|||
openSerial (&opts, &state);
|
||||
}
|
||||
|
||||
//need error handling on opening rigctl so we don't exit or crash on disconnect
|
||||
if (opts.use_rigctl == 1)
|
||||
{
|
||||
opts.rigctl_sockfd = Connect(opts.rigctlhostname, opts.rigctlportno);
|
||||
long int initfreq = 0;
|
||||
GetCurrentFreq(opts.rigctl_sockfd, initfreq);
|
||||
}
|
||||
|
||||
if((strncmp(opts.audio_in_dev, "tcp", 3) == 0)) //tcp socket input from SDR++ and others
|
||||
{
|
||||
//use same handling as connect function from rigctl
|
||||
//also still needs err handling
|
||||
opts.tcp_sockfd = Connect(opts.tcp_hostname, opts.tcp_portno);
|
||||
opts.audio_in_type = 8;
|
||||
}
|
||||
|
||||
//still need to work out why I can't use this
|
||||
//issues with samples received, may be using UDP DGRAMS incorrectly, incorrect procedure?
|
||||
if((strncmp(opts.audio_in_dev, "udp", 3) == 0)) //udp socket input from SDR++, GQRX, and others
|
||||
{
|
||||
//also still needs err handling
|
||||
opts.udp_sockfd = UDPBind(opts.udp_hostname, opts.udp_portno);
|
||||
opts.audio_in_type = 6;
|
||||
}
|
||||
|
||||
if((strncmp(opts.audio_in_dev, "pulse", 5) == 0))
|
||||
{
|
||||
opts.audio_in_type = 0;
|
||||
|
|
|
|||
|
|
@ -95,8 +95,8 @@ char * SyncTypes[44] = {
|
|||
"DMR ",
|
||||
"DMR ",
|
||||
"DMR ",
|
||||
"PROVOICE",
|
||||
"PROVOICE",
|
||||
"EDACS/PV",
|
||||
"EDACS/PV",
|
||||
"NXDN VOICE", //DATA
|
||||
"NXDN VOICE", //DATA
|
||||
"DSTAR HD",
|
||||
|
|
@ -118,8 +118,8 @@ char * SyncTypes[44] = {
|
|||
"DMR RC DATA",
|
||||
"P25P2",
|
||||
"P25P2",
|
||||
"FDMA", //37
|
||||
"TDMA", //38
|
||||
"EDACS/PV", //37
|
||||
"EDACS/PV", //38
|
||||
"",
|
||||
""
|
||||
};
|
||||
|
|
@ -416,6 +416,10 @@ void ncursesOpen (dsd_opts * opts, dsd_state * state)
|
|||
//ncursesMenu
|
||||
void ncursesMenu (dsd_opts * opts, dsd_state * state)
|
||||
{
|
||||
|
||||
//update sync time on cc sync so we don't immediately go CC hunting when exiting the menu
|
||||
state->last_cc_sync_time = time(NULL);
|
||||
|
||||
//close pulse output if not null output
|
||||
if (opts->audio_out == 1 && opts->audio_out_type == 0)
|
||||
{
|
||||
|
|
@ -427,6 +431,16 @@ void ncursesMenu (dsd_opts * opts, dsd_state * state)
|
|||
closePulseInput(opts);
|
||||
}
|
||||
|
||||
if (opts->audio_in_type == 8) //close TCP input SF file so we don't buffer audio while not decoding
|
||||
{
|
||||
sf_close(opts->tcp_file_in);
|
||||
}
|
||||
|
||||
if (opts->audio_in_type == 5) //close UDP input SF file so we don't buffer audio while not decoding
|
||||
{
|
||||
sf_close(opts->udp_file_in); //disable for testing
|
||||
}
|
||||
|
||||
state->payload_keyid = 0;
|
||||
state->payload_keyidR = 0;
|
||||
|
||||
|
|
@ -1089,7 +1103,7 @@ void ncursesMenu (dsd_opts * opts, dsd_state * state)
|
|||
resetState (state); //use sparingly, may cause memory leak
|
||||
state->samplesPerSymbol = 5;
|
||||
state->symbolCenter = 2;
|
||||
sprintf (opts->output_name, "ProVoice");
|
||||
sprintf (opts->output_name, "EDACS/PV");
|
||||
opts->dmr_stereo = 0; //this value is the end user option
|
||||
state->dmr_stereo = 0; //this values toggles on and off depending on voice or data handling
|
||||
opts->pulse_digi_rate_out = 8000;
|
||||
|
|
@ -1550,6 +1564,18 @@ void ncursesMenu (dsd_opts * opts, dsd_state * state)
|
|||
}
|
||||
#endif
|
||||
|
||||
if (opts->audio_in_type == 8) //re-open TCP input 'file'
|
||||
{
|
||||
opts->tcp_file_in = sf_open_fd(opts->tcp_sockfd, SFM_READ, opts->audio_in_file_info, 0);
|
||||
}
|
||||
|
||||
if (opts->audio_in_type == 5) //re-open UDP input 'file'
|
||||
{
|
||||
opts->udp_file_in = sf_open_fd(opts->udp_sockfd, SFM_READ, opts->audio_in_file_info, 0);
|
||||
}
|
||||
|
||||
//update sync time on cc sync so we don't immediately go CC hunting when exiting the menu
|
||||
state->last_cc_sync_time = time(NULL);
|
||||
|
||||
}
|
||||
//end Ncurses Menu
|
||||
|
|
@ -1592,7 +1618,7 @@ ncursesPrinter (dsd_opts * opts, dsd_state * state)
|
|||
}
|
||||
|
||||
//set lls sync types
|
||||
if (state->synctype >= 0 && state->synctype < 37) //not sure if this will be okay
|
||||
if (state->synctype >= 0 && state->synctype < 39)
|
||||
{
|
||||
lls = state->synctype;
|
||||
}
|
||||
|
|
@ -1715,25 +1741,19 @@ ncursesPrinter (dsd_opts * opts, dsd_state * state)
|
|||
}
|
||||
|
||||
//Call History Matrix Shuffling
|
||||
//ProVoice
|
||||
if ( (lls == 14 || lls == 15) && (time(NULL) - call_matrix[9][5] > 5) && state->carrier == 1)
|
||||
//Edacs - ProVoice
|
||||
if ( (lls == 14 || lls == 15 || lls == 37 || lls == 38) && state->carrier == 1)
|
||||
{
|
||||
for (short int k = 0; k < 9; k++)
|
||||
|
||||
if (state->edacs_vc_lcn != -1)
|
||||
{
|
||||
call_matrix[k][0] = call_matrix[k+1][0];
|
||||
call_matrix[k][1] = call_matrix[k+1][1];
|
||||
call_matrix[k][2] = call_matrix[k+1][2];
|
||||
call_matrix[k][3] = call_matrix[k+1][3];
|
||||
call_matrix[k][4] = call_matrix[k+1][4];
|
||||
call_matrix[k][5] = call_matrix[k+1][5];
|
||||
}
|
||||
|
||||
call_matrix[9][0] = lls;
|
||||
call_matrix[9][1] = 1;
|
||||
call_matrix[9][2] = 1;
|
||||
call_matrix[9][3] = 1;
|
||||
call_matrix[9][4] = 1;
|
||||
call_matrix[9][5] = time(NULL);
|
||||
call_matrix[state->edacs_vc_lcn][0] = lls;
|
||||
call_matrix[state->edacs_vc_lcn][1] = state->edacs_vc_lcn;
|
||||
call_matrix[state->edacs_vc_lcn][2] = state->lasttg;
|
||||
call_matrix[state->edacs_vc_lcn][3] = state->lastsrc;
|
||||
call_matrix[state->edacs_vc_lcn][4] = 1;
|
||||
call_matrix[state->edacs_vc_lcn][5] = time(NULL);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
|
@ -2022,7 +2042,15 @@ ncursesPrinter (dsd_opts * opts, dsd_state * state)
|
|||
if (opts->dmr_stereo_wav == 1) //opts->wav_out_file[0] != 0 &&
|
||||
{
|
||||
printw ("| Per Call - %s\n", opts->wav_out_file);
|
||||
printw ("| Per Call - %s\n", opts->wav_out_fileR);
|
||||
if (opts->dmr_stereo == 1) printw ("| Per Call - %s\n", opts->wav_out_fileR);
|
||||
}
|
||||
if (opts->use_rigctl == 1)
|
||||
{
|
||||
printw ("| RIGCTL Remote Control Client Established on Port [%d]\n", opts->rigctlportno);
|
||||
}
|
||||
if (opts->p25_trunk == 1 && (opts->use_rigctl == 1 || opts->audio_in_type == 3) )
|
||||
{
|
||||
printw ("| Trunk Tracking Active (P25/EDACS)\n");
|
||||
}
|
||||
|
||||
|
||||
|
|
@ -2539,7 +2567,93 @@ ncursesPrinter (dsd_opts * opts, dsd_state * state)
|
|||
printw ("TID: [%s] RID: [%s] \n", state->dpmr_target_id, state->dpmr_caller_id);
|
||||
}
|
||||
|
||||
if (lls == 6 || lls == 7 || lls == 18 || lls == 19 || lls == 14 || lls == 15)
|
||||
//EDACS and ProVoice
|
||||
if (lls == 14 || lls == 15 || lls == 37 || lls == 38)
|
||||
{
|
||||
attroff (COLOR_PAIR(3)); //colors off for EDACS
|
||||
if (state->edacs_site_id != 0)
|
||||
{
|
||||
if (opts->p25_is_tuned == 0)
|
||||
{
|
||||
printw ("| Monitoring Control Channel\n");
|
||||
}
|
||||
else
|
||||
{
|
||||
printw ("| Monitoring Voice Channel - LCN [%02d]\n", state->edacs_tuned_lcn);
|
||||
//since we are tuned, keep updating the time so it doesn't disappear during call
|
||||
call_matrix[state->edacs_tuned_lcn][5] = time(NULL);
|
||||
}
|
||||
printw ("| SITE [%03lld][%02llX]", state->edacs_site_id, state->edacs_site_id);
|
||||
|
||||
if (state->ea_mode == 1)
|
||||
{
|
||||
printw (" Extended Addressing");
|
||||
}
|
||||
else printw (" Standard/Networked");
|
||||
if (state->esk_mask == 0xA0)
|
||||
{
|
||||
printw (" w/ ESK");
|
||||
}
|
||||
else printw (" w/o ESK");
|
||||
printw ("\n");
|
||||
}
|
||||
for (i = 1; i <= state->edacs_lcn_count; i++)
|
||||
{
|
||||
//shim 443 afs in here for display purposes
|
||||
int a = (call_matrix[i][3] >> 7) & 0xF;
|
||||
int fs = call_matrix[i][3] & 0x7F;
|
||||
printw ("| - LCN [%02d][%.06lf]", i, (double)state->trunk_lcn_freq[i-1]/1000000);
|
||||
|
||||
//print Control Channel on LCN line with the current Control Channel
|
||||
if ( (i) == state->edacs_cc_lcn)
|
||||
{
|
||||
attron (COLOR_PAIR(1)); //yellow
|
||||
printw (" Control Channel");
|
||||
attroff (COLOR_PAIR(1));
|
||||
}
|
||||
//print active calls on corresponding LCN line
|
||||
if ((i != state->edacs_cc_lcn) && time(NULL) - call_matrix[i][5] < 2)
|
||||
{
|
||||
attron (COLOR_PAIR(3));
|
||||
if (state->ea_mode == 1) printw (" TG [%5lld] SRC [%8lld]", call_matrix[i][2], call_matrix[i][3] );
|
||||
else printw (" AFS [%3llX][%02d-%03d]", call_matrix[i][3], a, fs );
|
||||
for (int k = 0; k < state->group_tally; k++)
|
||||
{
|
||||
if (state->group_array[k].groupNumber == call_matrix[i][2])
|
||||
{
|
||||
printw (" [%s]", state->group_array[k].groupName);
|
||||
printw ("[%s]", state->group_array[k].groupMode);
|
||||
}
|
||||
}
|
||||
attroff (COLOR_PAIR(3));
|
||||
}
|
||||
//print dying or dead calls in red for x seconds longer
|
||||
if ( (i != state->edacs_cc_lcn) && (time(NULL) - call_matrix[i][5] >= 2) && (time(NULL) - call_matrix[i][5] < 5) )
|
||||
{
|
||||
attron (COLOR_PAIR(2));
|
||||
if (state->ea_mode == 1) printw (" TG [%5lld] SRC [%8lld]", call_matrix[i][2], call_matrix[i][3] );
|
||||
else printw (" AFS [%3llX][%02d-%03d]", call_matrix[i][3], a, fs );
|
||||
for (int k = 0; k < state->group_tally; k++)
|
||||
{
|
||||
if (state->group_array[k].groupNumber == call_matrix[i][2])
|
||||
{
|
||||
printw (" [%s]", state->group_array[k].groupName);
|
||||
printw ("[%s]", state->group_array[k].groupMode);
|
||||
}
|
||||
}
|
||||
attroff (COLOR_PAIR(2));
|
||||
}
|
||||
if (i == state->edacs_tuned_lcn && opts->p25_is_tuned == 1) printw (" **T**"); //asterisk which lcn is opened
|
||||
printw ("\n");
|
||||
}
|
||||
if (state->carrier == 1)
|
||||
{
|
||||
attron (COLOR_PAIR(3));
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
if (lls == 6 || lls == 7 || lls == 18 || lls == 19)
|
||||
{
|
||||
printw ("| %s ", SyncTypes[lls]);
|
||||
//printw ("%s", state->dmr_branding);
|
||||
|
|
@ -2558,8 +2672,8 @@ ncursesPrinter (dsd_opts * opts, dsd_state * state)
|
|||
printw ("--Call History----------------------------------------------------------------\n");
|
||||
for (short int j = 0; j < 10; j++)
|
||||
{
|
||||
//only print if a valid time was assinged to the matrix
|
||||
if ( ((time(NULL) - call_matrix[9-j][5]) < 9999) )
|
||||
//only print if a valid time was assigned to the matrix, and not EDACS/PV
|
||||
if ( ((time(NULL) - call_matrix[9-j][5]) < 9999) && call_matrix[9-j][0] != 14 && call_matrix[9-j][0] != 15 && call_matrix[9-j][0] != 37 && call_matrix[9-j][0] != 38 )
|
||||
{
|
||||
printw ("| %s ", SyncTypes[call_matrix[9-j][0]]);
|
||||
if (lls == 8 || lls == 9 || lls == 16 || lls == 17)
|
||||
|
|
@ -2602,6 +2716,21 @@ ncursesPrinter (dsd_opts * opts, dsd_state * state)
|
|||
printw ("%s ", getDateC(call_matrix[9-j][5]) ); //You're welcome
|
||||
printw ("%s \n", getTimeC(call_matrix[9-j][5]) ); //Roman
|
||||
}
|
||||
|
||||
//EDACS and ProVoice, outside of timestamp loop
|
||||
if (call_matrix[j][0] == 14 || call_matrix[j][0] == 15 || call_matrix[j][0] == 37 || call_matrix[j][0] == 38 )
|
||||
{
|
||||
if (call_matrix[j][3] != 0)
|
||||
{
|
||||
printw ("| %s ", SyncTypes[call_matrix[j][0]]);
|
||||
printw ("LCN [%2lld] ", call_matrix[j][1]);
|
||||
printw ("Group [%8lld] ", call_matrix[j][2]);
|
||||
printw ("Source [%8lld] ", call_matrix[j][3]);
|
||||
printw ("%s ", getDateC(call_matrix[j][5]) );
|
||||
printw ("%s \n", getTimeC(call_matrix[j][5]) );
|
||||
}
|
||||
|
||||
}
|
||||
} //end Call History
|
||||
//fence bottom
|
||||
printw ("------------------------------------------------------------------------------\n");
|
||||
|
|
|
|||
|
|
@ -0,0 +1,285 @@
|
|||
/*-------------------------------------------------------------------------------
|
||||
* dsd_rigctl.c
|
||||
* Simple RIGCTL Client for DSD (remote control of GQRX, SDR++, etc)
|
||||
*
|
||||
* Portions from https://github.com/neural75/gqrx-scanner
|
||||
*
|
||||
* LWVMOBILE
|
||||
* 2022-10 DSD-FME Florida Man Edition
|
||||
*-----------------------------------------------------------------------------*/
|
||||
|
||||
#include "dsd.h"
|
||||
#include <sys/types.h>
|
||||
#include <sys/socket.h>
|
||||
#include <netinet/in.h>
|
||||
#include <netdb.h>
|
||||
|
||||
//UDP Specific
|
||||
#include <arpa/inet.h>
|
||||
|
||||
#define BUFSIZE 1024
|
||||
#define FREQ_MAX 4096
|
||||
#define SAVED_FREQ_MAX 1000
|
||||
#define TAG_MAX 100
|
||||
|
||||
//
|
||||
// error - wrapper for perror
|
||||
//
|
||||
void error(char *msg) {
|
||||
perror(msg);
|
||||
exit(0);
|
||||
}
|
||||
|
||||
//
|
||||
// Connect
|
||||
//
|
||||
int Connect (char *hostname, int portno)
|
||||
{
|
||||
int sockfd, n;
|
||||
struct sockaddr_in serveraddr;
|
||||
struct hostent *server;
|
||||
|
||||
|
||||
/* socket: create the socket */
|
||||
sockfd = socket(AF_INET, SOCK_STREAM, 0);
|
||||
if (sockfd < 0)
|
||||
{
|
||||
fprintf(stderr,"ERROR opening socket\n");
|
||||
error("ERROR opening socket");
|
||||
}
|
||||
|
||||
|
||||
/* gethostbyname: get the server's DNS entry */
|
||||
server = gethostbyname(hostname);
|
||||
if (server == NULL) {
|
||||
fprintf(stderr,"ERROR, no such host as %s\n", hostname);
|
||||
exit(0);
|
||||
}
|
||||
|
||||
/* build the server's Internet address */
|
||||
bzero((char *) &serveraddr, sizeof(serveraddr));
|
||||
serveraddr.sin_family = AF_INET;
|
||||
bcopy((char *)server->h_addr,
|
||||
(char *)&serveraddr.sin_addr.s_addr, server->h_length);
|
||||
serveraddr.sin_port = htons(portno);
|
||||
|
||||
/* connect: create a connection with the server */
|
||||
if (connect(sockfd, (const struct sockaddr *) &serveraddr, sizeof(serveraddr)) < 0)
|
||||
error("ERROR connecting");
|
||||
|
||||
return sockfd;
|
||||
}
|
||||
//
|
||||
// Send
|
||||
//
|
||||
bool Send(int sockfd, char *buf)
|
||||
{
|
||||
int n;
|
||||
|
||||
n = write(sockfd, buf, strlen(buf));
|
||||
if (n < 0)
|
||||
error("ERROR writing to socket");
|
||||
return true;
|
||||
}
|
||||
|
||||
//
|
||||
// Recv
|
||||
//
|
||||
bool Recv(int sockfd, char *buf)
|
||||
{
|
||||
int n;
|
||||
|
||||
n = read(sockfd, buf, BUFSIZE);
|
||||
if (n < 0)
|
||||
error("ERROR reading from socket");
|
||||
buf[n]= '\0';
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
//
|
||||
// GQRX Protocol
|
||||
//
|
||||
// bool GetCurrentFreq(int sockfd, freq_t *freq)
|
||||
bool GetCurrentFreq(int sockfd, long int freq) //compatible with P25 freq storage
|
||||
{
|
||||
char buf[BUFSIZE];
|
||||
char * ptr;
|
||||
char * token;
|
||||
|
||||
Send(sockfd, "f\n");
|
||||
Recv(sockfd, buf); //buffer will contain a stored \n line break as well
|
||||
|
||||
if (strcmp(buf, "RPRT 1") == 0 ) //sdr++ may return this in error
|
||||
return false;
|
||||
|
||||
token = strtok (buf, "\n"); //remove line break \n from return buf string
|
||||
freq = strtol (token, &ptr, 10); //number is base, as in base 2 is binary, base 10 is dec, base 16 hex
|
||||
//fprintf (stderr, "\nRIGCTL Buffer: [%s]\n", buf);
|
||||
//fprintf (stderr, "\nRIGCTL VFO Freq: [%ld]\n", freq);
|
||||
return true;
|
||||
}
|
||||
// bool SetFreq(int sockfd, freq_t freq)
|
||||
bool SetFreq(int sockfd, long int freq)
|
||||
{
|
||||
char buf[BUFSIZE];
|
||||
|
||||
sprintf (buf, "F %ld\n", freq); //llu
|
||||
Send(sockfd, buf);
|
||||
Recv(sockfd, buf);
|
||||
|
||||
if (strcmp(buf, "RPRT 1") == 0 )
|
||||
return false;
|
||||
|
||||
GetCurrentFreq(sockfd, freq);
|
||||
|
||||
//freq_t freq_current = 0;
|
||||
//long int freq_current = 0;
|
||||
// do
|
||||
// {
|
||||
// GetCurrentFreq(sockfd, freq_current);
|
||||
// } while (freq_current != freq);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool SetModulation(int sockfd, int bandwidth)
|
||||
{
|
||||
char buf[BUFSIZE];
|
||||
//ideally, on P25, we want to use the iden_up with bw, and calc bandwidth first
|
||||
//bandwidth = 12500; //default value, if doing pV, may want to swtich to 25000
|
||||
sprintf (buf, "M FM %d\n", bandwidth);
|
||||
Send(sockfd, buf);
|
||||
Recv(sockfd, buf);
|
||||
|
||||
if (strcmp(buf, "RPRT 1") == 0 )
|
||||
return false;
|
||||
|
||||
//GetCurrentFreq(sockfd, freq);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool GetSignalLevel(int sockfd, double *dBFS)
|
||||
{
|
||||
char buf[BUFSIZE];
|
||||
|
||||
Send(sockfd, "l\n");
|
||||
Recv(sockfd, buf);
|
||||
|
||||
if (strcmp(buf, "RPRT 1") == 0 )
|
||||
return false;
|
||||
|
||||
sscanf(buf, "%lf", dBFS);
|
||||
*dBFS = round((*dBFS) * 10)/10;
|
||||
|
||||
if (*dBFS == 0.0)
|
||||
return false;
|
||||
return true;
|
||||
}
|
||||
|
||||
bool GetSquelchLevel(int sockfd, double *dBFS)
|
||||
{
|
||||
char buf[BUFSIZE];
|
||||
|
||||
Send(sockfd, "l SQL\n");
|
||||
Recv(sockfd, buf);
|
||||
|
||||
if (strcmp(buf, "RPRT 1") == 0 )
|
||||
return false;
|
||||
|
||||
sscanf(buf, "%lf", dBFS);
|
||||
*dBFS = round((*dBFS) * 10)/10;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool SetSquelchLevel(int sockfd, double dBFS)
|
||||
{
|
||||
char buf[BUFSIZE];
|
||||
|
||||
sprintf (buf, "L SQL %f\n", dBFS);
|
||||
Send(sockfd, buf);
|
||||
Recv(sockfd, buf);
|
||||
|
||||
if (strcmp(buf, "RPRT 1") == 0 )
|
||||
return false;
|
||||
|
||||
return true;
|
||||
}
|
||||
//
|
||||
// GetSignalLevelEx
|
||||
// Get a bunch of sample with some delay and calculate the mean value
|
||||
//
|
||||
bool GetSignalLevelEx(int sockfd, double *dBFS, int n_samp)
|
||||
{
|
||||
double temp_level;
|
||||
*dBFS = 0;
|
||||
int errors = 0;
|
||||
for (int i = 0; i < n_samp; i++)
|
||||
{
|
||||
if ( GetSignalLevel(sockfd, &temp_level) )
|
||||
*dBFS = *dBFS + temp_level;
|
||||
else
|
||||
errors++;
|
||||
usleep(1000);
|
||||
}
|
||||
*dBFS = *dBFS / (n_samp - errors);
|
||||
return true;
|
||||
}
|
||||
|
||||
//shoe in UDP input connection here...still having issues that I don't know how to resolve
|
||||
int UDPBind (char *hostname, int portno)
|
||||
{
|
||||
int sockfd, n;
|
||||
struct sockaddr_in serveraddr, client_addr;
|
||||
struct hostent *server;
|
||||
|
||||
/* socket: create the socket */
|
||||
//UDP socket
|
||||
sockfd = socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP);
|
||||
|
||||
if (sockfd < 0)
|
||||
{
|
||||
fprintf(stderr,"ERROR opening UDP socket\n");
|
||||
error("ERROR opening UDP socket");
|
||||
}
|
||||
|
||||
/* build the server's Internet address */
|
||||
bzero((char *) &serveraddr, sizeof(serveraddr));
|
||||
serveraddr.sin_family = AF_INET;
|
||||
serveraddr.sin_addr.s_addr = INADDR_ANY; //INADDR_ANY
|
||||
serveraddr.sin_port = htons(portno);
|
||||
|
||||
//Bind socket to listening
|
||||
if (bind(sockfd, (struct sockaddr *) &serveraddr, sizeof(serveraddr)) < 0) {
|
||||
perror("ERROR on binding UDP Port");
|
||||
}
|
||||
|
||||
return sockfd;
|
||||
}
|
||||
|
||||
void rtl_udp_tune(dsd_opts * opts, dsd_state * state, long int frequency)
|
||||
{
|
||||
int handle;
|
||||
unsigned short udp_port = opts->rtl_udp_port;
|
||||
char data[5] = {0}; //data buffer size is 5 for UDP frequency tuning
|
||||
struct sockaddr_in address;
|
||||
|
||||
uint32_t new_freq = frequency;
|
||||
opts->rtlsdr_center_freq = new_freq; //for ncurses terminal display after rtl is started up
|
||||
|
||||
handle = socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP);
|
||||
|
||||
data[0] = 0;
|
||||
data[1] = new_freq & 0xFF;
|
||||
data[2] = (new_freq >> 8) & 0xFF;
|
||||
data[3] = (new_freq >> 16) & 0xFF;
|
||||
data[4] = (new_freq >> 24) & 0xFF;
|
||||
|
||||
memset((char * ) & address, 0, sizeof(address));
|
||||
address.sin_family = AF_INET;
|
||||
address.sin_addr.s_addr = inet_addr("127.0.0.1"); //make user configurable later
|
||||
address.sin_port = htons(udp_port);
|
||||
sendto(handle, data, 5, 0, (const struct sockaddr * ) & address, sizeof(struct sockaddr_in));
|
||||
}
|
||||
|
|
@ -100,32 +100,34 @@ getSymbol (dsd_opts * opts, dsd_state * state, int have_sync)
|
|||
}
|
||||
|
||||
}
|
||||
else if (opts->audio_in_type == 1) {
|
||||
result = sf_read_short(opts->audio_in_file, &sample, 1);
|
||||
//fprintf (stderr, "..");
|
||||
if (opts->monitor_input_audio == 1 && state->lastsynctype == -1 && sample < 32767 && sample > -32767)
|
||||
{
|
||||
state->pulse_raw_out_buffer = sample; //steal raw out buffer sample here?
|
||||
pa_simple_write(opts->pulse_raw_dev_out, (void*)&state->pulse_raw_out_buffer, 2, NULL);
|
||||
}
|
||||
if(result == 0) {
|
||||
cleanupAndExit (opts, state);
|
||||
}
|
||||
//stdin only, wav files moving to new number
|
||||
else if (opts->audio_in_type == 1)
|
||||
{
|
||||
result = sf_read_short(opts->audio_in_file, &sample, 1);
|
||||
if(result == 0)
|
||||
{
|
||||
//cleanupAndExit (opts, state);
|
||||
sf_close(opts->audio_in_file);
|
||||
opts->audio_in_type = 0; //set input type
|
||||
openPulseInput(opts); //open pulse inpput
|
||||
sample = 0; //send zero sample
|
||||
}
|
||||
}
|
||||
//wav files, same but using seperate value so we can still manipulate ncurses menu
|
||||
//since we can not worry about getch/stdin conflict
|
||||
else if (opts->audio_in_type == 2)
|
||||
{
|
||||
#ifdef USE_PORTAUDIO
|
||||
PaError err = Pa_ReadStream( opts->audio_in_pa_stream, &sample, 1 );
|
||||
if( err != paNoError )
|
||||
result = sf_read_short(opts->audio_in_file, &sample, 1);
|
||||
if(result == 0)
|
||||
{
|
||||
fprintf( stderr, "An error occured while using the portaudio input stream\n" );
|
||||
fprintf( stderr, "Error number: %d\n", err );
|
||||
fprintf( stderr, "Error message: %s\n", Pa_GetErrorText( err ) );
|
||||
|
||||
sf_close(opts->audio_in_file);
|
||||
opts->audio_in_type = 0; //set input type
|
||||
openPulseInput(opts); //open pulse inpput
|
||||
sample = 0; //send zero sample
|
||||
|
||||
}
|
||||
|
||||
|
||||
#endif
|
||||
}
|
||||
}
|
||||
else if (opts->audio_in_type == 3)
|
||||
{
|
||||
#ifdef USE_RTLSDR
|
||||
|
|
@ -140,12 +142,30 @@ getSymbol (dsd_opts * opts, dsd_state * state, int have_sync)
|
|||
|
||||
#endif
|
||||
}
|
||||
//tcp socket input from SDR++
|
||||
else if (opts->audio_in_type == 8)
|
||||
{
|
||||
result = sf_read_short(opts->tcp_file_in, &sample, 1);
|
||||
if(result == 0) {
|
||||
sf_close(opts->tcp_file_in);
|
||||
opts->audio_in_type = 0; //set input type
|
||||
openPulseInput(opts); //open pulse inpput
|
||||
sample = 0; //zero sample on bad result, keep the ball rolling
|
||||
fprintf (stderr, "Connection to TCP Server Disconnected.\n");
|
||||
}
|
||||
}
|
||||
//UDP Socket input...not working correct. Reads samples, but no sync
|
||||
else if (opts->audio_in_type == 6)
|
||||
{
|
||||
//I think this doesn't get the entire dgram when we run sf_read_short on the udp dgram
|
||||
result = sf_read_short(opts->udp_file_in, &sample, 1);
|
||||
// if (sample != 0)
|
||||
// fprintf (stderr, "Result = %d Sample = %d \n", result, sample);
|
||||
}
|
||||
|
||||
|
||||
|
||||
#ifdef TRACE_DSD
|
||||
state->debug_sample_index++;
|
||||
#endif
|
||||
|
||||
// printf ("res: %zd\n, offset: %lld", result, sf_seek(opts->audio_in_file, 0, SEEK_CUR));
|
||||
if (opts->use_cosine_filter)
|
||||
{
|
||||
if ( (state->lastsynctype >= 10 && state->lastsynctype <= 13) || state->lastsynctype == 32 || state->lastsynctype == 33 || state->lastsynctype == 34)
|
||||
|
|
@ -386,11 +406,11 @@ getSymbol (dsd_opts * opts, dsd_state * state, int have_sync)
|
|||
if (state->rf_mod == 2) //GFSK
|
||||
{
|
||||
symbol = state->symbolc;
|
||||
if (state->symbolc == 0 && state->synctype >= 0)
|
||||
if (state->symbolc == 0 )
|
||||
{
|
||||
symbol = -3; //-1
|
||||
}
|
||||
if (state->symbolc == 1 && state->synctype >= 0)
|
||||
if (state->symbolc == 1 )
|
||||
{
|
||||
symbol = -1; //-3
|
||||
}
|
||||
|
|
|
|||
|
|
@ -0,0 +1,517 @@
|
|||
/*
|
||||
* File: bch3.h
|
||||
* Title: Encoder/decoder for binary BCH codes in C (Version 3.1)
|
||||
* Author: Robert Morelos-Zaragoza
|
||||
* Date: August 1994-June 13, 1997
|
||||
* Mod: December 14, 2021, for EDACS-FM BCH Polynomial Error Generation and Detection
|
||||
* Mod2: October 2022, for DSD-FME port of EDACS-FM
|
||||
* Source: www.eccpage.com
|
||||
*
|
||||
* =============== Encoder/Decoder for binary BCH codes in C =================
|
||||
*
|
||||
* Version 1: Original program. The user provides the generator polynomial
|
||||
* of the code (cumbersome!).
|
||||
* Version 2: Computes the generator polynomial of the code.
|
||||
* Version 3: No need to input the coefficients of a primitive polynomial of
|
||||
* degree m, used to construct the Galois Field GF(2**m). The
|
||||
* program now works for any binary BCH code of length such that:
|
||||
* 2**(m-1) - 1 < length <= 2**m - 1
|
||||
*
|
||||
* Note: You may have to change the size of the arrays to make it work.
|
||||
*
|
||||
* The encoding and decoding methods used in this program are based on the
|
||||
* book "Error Control Coding: Fundamentals and Applications", by Lin and
|
||||
* Costello, Prentice Hall, 1983.
|
||||
*
|
||||
* Thanks to Patrick Boyle (pboyle@era.com) for his observation that 'bch2.c'
|
||||
* did not work for lengths other than 2**m-1 which led to this new version.
|
||||
* Portions of this program are from 'rs.c', a Reed-Solomon encoder/decoder
|
||||
* in C, written by Simon Rockliff (simon@augean.ua.oz.au) on 21/9/89. The
|
||||
* previous version of the BCH encoder/decoder in C, 'bch2.c', was written by
|
||||
* Robert Morelos-Zaragoza (robert@spectra.eng.hawaii.edu) on 5/19/92.
|
||||
*
|
||||
* NOTE:
|
||||
* The author is not responsible for any malfunctioning of
|
||||
* this program, nor for any damage caused by it. Please include the
|
||||
* original program along with these comments in any redistribution.
|
||||
*
|
||||
* For more information, suggestions, or other ideas on implementing error
|
||||
* correcting codes, please contact me at:
|
||||
*
|
||||
* Robert Morelos-Zaragoza
|
||||
* 5120 Woodway, Suite 7036
|
||||
* Houston, Texas 77056
|
||||
*
|
||||
* email: r.morelos-zaragoza@ieee.org
|
||||
*
|
||||
* COPYRIGHT NOTICE: This computer program is free for non-commercial purposes.
|
||||
* You may implement this program for any non-commercial application. You may
|
||||
* also implement this program for commercial purposes, provided that you
|
||||
* obtain my written permission. Any modification of this program is covered
|
||||
* by this copyright.
|
||||
*
|
||||
* == Copyright (c) 1994-7, Robert Morelos-Zaragoza. All rights reserved. ==
|
||||
*
|
||||
* m = order of the Galois field GF(2**m)
|
||||
* n = 2**m - 1 = size of the multiplicative group of GF(2**m)
|
||||
* length = length of the BCH code
|
||||
* t = error correcting capability (max. no. of errors the code corrects)
|
||||
* d = 2*t + 1 = designed min. distance = no. of consecutive roots of g(x) + 1
|
||||
* k = n - deg(g(x)) = dimension (no. of information bits/codeword) of the code
|
||||
* p[] = coefficients of a primitive polynomial used to generate GF(2**m)
|
||||
* g[] = coefficients of the generator polynomial, g(x)
|
||||
* alpha_to [] = log table of GF(2**m)
|
||||
* index_of[] = antilog table of GF(2**m)
|
||||
* ddata[] = information bits = coefficients of data polynomial, i(x)
|
||||
* bb[] = coefficients of redundancy polynomial x^(length-k) i(x) modulo g(x)
|
||||
* numerr = number of errors
|
||||
* errpos[] = error positions
|
||||
* recd[] = coefficients of the received polynomial
|
||||
* decerror = number of decoding errors (in _message_ positions)
|
||||
*
|
||||
*/
|
||||
|
||||
#include <math.h>
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
|
||||
int m, n, length, k, t, d;
|
||||
int p[21];
|
||||
int alpha_to[1048576], index_of[1048576], g[548576];
|
||||
int recd[1048576], ddata[1048576], bb[548576];
|
||||
int seed;
|
||||
int numerr, errpos[1024], decerror = 0;
|
||||
|
||||
|
||||
void
|
||||
read_p()
|
||||
/*
|
||||
* Read m, the degree of a primitive polynomial p(x) used to compute the
|
||||
* Galois field GF(2**m). Get precomputed coefficients p[] of p(x). Read
|
||||
* the code length.
|
||||
*/
|
||||
{
|
||||
int i, ninf;
|
||||
|
||||
do {
|
||||
|
||||
m = 6;
|
||||
} while ( !(m>1) || !(m<21) );
|
||||
for (i=1; i<m; i++)
|
||||
p[i] = 0;
|
||||
p[0] = p[m] = 1;
|
||||
if (m == 2) p[1] = 1;
|
||||
else if (m == 3) p[1] = 1;
|
||||
else if (m == 4) p[1] = 1;
|
||||
else if (m == 5) p[2] = 1;
|
||||
else if (m == 6) p[1] = 1;
|
||||
else if (m == 7) p[1] = 1;
|
||||
else if (m == 8) p[4] = p[5] = p[6] = 1;
|
||||
else if (m == 9) p[4] = 1;
|
||||
else if (m == 10) p[3] = 1;
|
||||
else if (m == 11) p[2] = 1;
|
||||
else if (m == 12) p[3] = p[4] = p[7] = 1;
|
||||
else if (m == 13) p[1] = p[3] = p[4] = 1;
|
||||
else if (m == 14) p[1] = p[11] = p[12] = 1;
|
||||
else if (m == 15) p[1] = 1;
|
||||
else if (m == 16) p[2] = p[3] = p[5] = 1;
|
||||
else if (m == 17) p[3] = 1;
|
||||
else if (m == 18) p[7] = 1;
|
||||
else if (m == 19) p[1] = p[5] = p[6] = 1;
|
||||
else if (m == 20) p[3] = 1;
|
||||
|
||||
n = 1;
|
||||
for (i = 0; i <= m; i++) {
|
||||
n *= 2;
|
||||
}
|
||||
n = n / 2 - 1;
|
||||
ninf = (n + 1) / 2 - 1;
|
||||
|
||||
do {
|
||||
|
||||
length = 40;
|
||||
} while ( !((length <= n)&&(length>ninf)) );
|
||||
}
|
||||
|
||||
|
||||
void
|
||||
generate_gf()
|
||||
/*
|
||||
* Generate field GF(2**m) from the irreducible polynomial p(X) with
|
||||
* coefficients in p[0]..p[m].
|
||||
*
|
||||
* Lookup tables:
|
||||
* index->polynomial form: alpha_to[] contains j=alpha^i;
|
||||
* polynomial form -> index form: index_of[j=alpha^i] = i
|
||||
*
|
||||
* alpha=2 is the primitive element of GF(2**m)
|
||||
*/
|
||||
{
|
||||
register int i, mask;
|
||||
|
||||
mask = 1;
|
||||
alpha_to[m] = 0;
|
||||
for (i = 0; i < m; i++) {
|
||||
alpha_to[i] = mask;
|
||||
index_of[alpha_to[i]] = i;
|
||||
if (p[i] != 0)
|
||||
alpha_to[m] ^= mask;
|
||||
mask <<= 1;
|
||||
}
|
||||
index_of[alpha_to[m]] = m;
|
||||
mask >>= 1;
|
||||
for (i = m + 1; i < n; i++) {
|
||||
if (alpha_to[i - 1] >= mask)
|
||||
alpha_to[i] = alpha_to[m] ^ ((alpha_to[i - 1] ^ mask) << 1);
|
||||
else
|
||||
alpha_to[i] = alpha_to[i - 1] << 1;
|
||||
index_of[alpha_to[i]] = i;
|
||||
}
|
||||
index_of[0] = -1;
|
||||
}
|
||||
|
||||
|
||||
void
|
||||
gen_poly()
|
||||
/*
|
||||
* Compute the generator polynomial of a binary BCH code. Fist generate the
|
||||
* cycle sets modulo 2**m - 1, cycle[][] = (i, 2*i, 4*i, ..., 2^l*i). Then
|
||||
* determine those cycle sets that contain integers in the set of (d-1)
|
||||
* consecutive integers {1..(d-1)}. The generator polynomial is calculated
|
||||
* as the product of linear factors of the form (x+alpha^i), for every i in
|
||||
* the above cycle sets.
|
||||
*/
|
||||
{
|
||||
register int ii, jj, ll, kaux;
|
||||
register int test, aux, nocycles, root, noterms, rdncy;
|
||||
int cycle[1024][21], size[1024], min[1024], zeros[1024];
|
||||
|
||||
/* Generate cycle sets modulo n, n = 2**m - 1 */
|
||||
cycle[0][0] = 0;
|
||||
size[0] = 1;
|
||||
cycle[1][0] = 1;
|
||||
size[1] = 1;
|
||||
jj = 1; /* cycle set index */
|
||||
if (m > 9) {
|
||||
|
||||
}
|
||||
do {
|
||||
/* Generate the jj-th cycle set */
|
||||
ii = 0;
|
||||
do {
|
||||
ii++;
|
||||
cycle[jj][ii] = (cycle[jj][ii - 1] * 2) % n;
|
||||
size[jj]++;
|
||||
aux = (cycle[jj][ii] * 2) % n;
|
||||
} while (aux != cycle[jj][0]);
|
||||
/* Next cycle set representative */
|
||||
ll = 0;
|
||||
do {
|
||||
ll++;
|
||||
test = 0;
|
||||
for (ii = 1; ((ii <= jj) && (!test)); ii++)
|
||||
/* Examine previous cycle sets */
|
||||
for (kaux = 0; ((kaux < size[ii]) && (!test)); kaux++)
|
||||
if (ll == cycle[ii][kaux])
|
||||
test = 1;
|
||||
} while ((test) && (ll < (n - 1)));
|
||||
if (!(test)) {
|
||||
jj++; /* next cycle set index */
|
||||
cycle[jj][0] = ll;
|
||||
size[jj] = 1;
|
||||
}
|
||||
} while (ll < (n - 1));
|
||||
nocycles = jj; /* number of cycle sets modulo n */
|
||||
|
||||
t = 2;
|
||||
|
||||
d = 2 * t + 1;
|
||||
|
||||
/* Search for roots 1, 2, ..., d-1 in cycle sets */
|
||||
kaux = 0;
|
||||
rdncy = 0;
|
||||
for (ii = 1; ii <= nocycles; ii++) {
|
||||
min[kaux] = 0;
|
||||
test = 0;
|
||||
for (jj = 0; ((jj < size[ii]) && (!test)); jj++)
|
||||
for (root = 1; ((root < d) && (!test)); root++)
|
||||
if (root == cycle[ii][jj]) {
|
||||
test = 1;
|
||||
min[kaux] = ii;
|
||||
}
|
||||
if (min[kaux]) {
|
||||
rdncy += size[min[kaux]];
|
||||
kaux++;
|
||||
}
|
||||
}
|
||||
noterms = kaux;
|
||||
kaux = 1;
|
||||
for (ii = 0; ii < noterms; ii++)
|
||||
for (jj = 0; jj < size[min[ii]]; jj++) {
|
||||
zeros[kaux] = cycle[min[ii]][jj];
|
||||
kaux++;
|
||||
}
|
||||
|
||||
k = length - rdncy;
|
||||
|
||||
if (k<0)
|
||||
{
|
||||
exit(0);
|
||||
}
|
||||
|
||||
/* Compute the generator polynomial */
|
||||
g[0] = alpha_to[zeros[1]];
|
||||
g[1] = 1; /* g(x) = (X + zeros[1]) initially */
|
||||
for (ii = 2; ii <= rdncy; ii++) {
|
||||
g[ii] = 1;
|
||||
for (jj = ii - 1; jj > 0; jj--)
|
||||
if (g[jj] != 0)
|
||||
g[jj] = g[jj - 1] ^ alpha_to[(index_of[g[jj]] + zeros[ii]) % n];
|
||||
else
|
||||
g[jj] = g[jj - 1];
|
||||
g[0] = alpha_to[(index_of[g[0]] + zeros[ii]) % n];
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
void
|
||||
encode_bch()
|
||||
/*
|
||||
* Compute redundacy bb[], the coefficients of b(x). The redundancy
|
||||
* polynomial b(x) is the remainder after dividing x^(length-k)*ddata(x)
|
||||
* by the generator polynomial g(x).
|
||||
*/
|
||||
{
|
||||
register int i, j;
|
||||
register int feedback;
|
||||
|
||||
for (i = 0; i < length - k; i++)
|
||||
bb[i] = 0;
|
||||
for (i = k - 1; i >= 0; i--) {
|
||||
feedback = ddata[i] ^ bb[length - k - 1];
|
||||
if (feedback != 0) {
|
||||
for (j = length - k - 1; j > 0; j--)
|
||||
if (g[j] != 0)
|
||||
bb[j] = bb[j - 1] ^ feedback;
|
||||
else
|
||||
bb[j] = bb[j - 1];
|
||||
bb[0] = g[0] && feedback;
|
||||
} else {
|
||||
for (j = length - k - 1; j > 0; j--)
|
||||
bb[j] = bb[j - 1];
|
||||
bb[0] = 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
void
|
||||
decode_bch()
|
||||
/*
|
||||
* Simon Rockliff's implementation of Berlekamp's algorithm.
|
||||
*
|
||||
* Assume we have received bits in recd[i], i=0..(n-1).
|
||||
*
|
||||
* Compute the 2*t syndromes by substituting alpha^i into rec(X) and
|
||||
* evaluating, storing the syndromes in s[i], i=1..2t (leave s[0] zero) .
|
||||
* Then we use the Berlekamp algorithm to find the error location polynomial
|
||||
* elp[i].
|
||||
*
|
||||
* If the degree of the elp is >t, then we cannot correct all the errors, and
|
||||
* we have detected an uncorrectable error pattern. We output the information
|
||||
* bits uncorrected.
|
||||
*
|
||||
* If the degree of elp is <=t, we substitute alpha^i , i=1..n into the elp
|
||||
* to get the roots, hence the inverse roots, the error location numbers.
|
||||
* This step is usually called "Chien's search".
|
||||
*
|
||||
* If the number of errors located is not equal the degree of the elp, then
|
||||
* the decoder assumes that there are more than t errors and cannot correct
|
||||
* them, only detect them. We output the information bits uncorrected.
|
||||
*/
|
||||
{
|
||||
register int i, j, u, q, t2, count = 0, syn_error = 0;
|
||||
int elp[1026][1024], d[1026], l[1026], u_lu[1026], s[1025];
|
||||
int root[200], loc[200], err[1024], reg[201];
|
||||
|
||||
t2 = 2 * t;
|
||||
|
||||
/* first form the syndromes */
|
||||
for (i = 1; i <= t2; i++) {
|
||||
s[i] = 0;
|
||||
for (j = 0; j < length; j++)
|
||||
if (recd[j] != 0)
|
||||
s[i] ^= alpha_to[(i * j) % n];
|
||||
if (s[i] != 0)
|
||||
syn_error = 1; /* set error flag if non-zero syndrome */
|
||||
/*
|
||||
* Note: If the code is used only for ERROR DETECTION, then
|
||||
* exit program here indicating the presence of errors.
|
||||
*/
|
||||
/* convert syndrome from polynomial form to index form */
|
||||
s[i] = index_of[s[i]];
|
||||
}
|
||||
|
||||
if (syn_error) { /* if there are errors, try to correct them */
|
||||
/*
|
||||
* Compute the error location polynomial via the Berlekamp
|
||||
* iterative algorithm. Following the terminology of Lin and
|
||||
* Costello's book : d[u] is the 'mu'th discrepancy, where
|
||||
* u='mu'+1 and 'mu' (the Greek letter!) is the step number
|
||||
* ranging from -1 to 2*t (see L&C), l[u] is the degree of
|
||||
* the elp at that step, and u_l[u] is the difference between
|
||||
* the step number and the degree of the elp.
|
||||
*/
|
||||
/* initialise table entries */
|
||||
d[0] = 0; /* index form */
|
||||
d[1] = s[1]; /* index form */
|
||||
elp[0][0] = 0; /* index form */
|
||||
elp[1][0] = 1; /* polynomial form */
|
||||
for (i = 1; i < t2; i++) {
|
||||
elp[0][i] = -1; /* index form */
|
||||
elp[1][i] = 0; /* polynomial form */
|
||||
}
|
||||
l[0] = 0;
|
||||
l[1] = 0;
|
||||
u_lu[0] = -1;
|
||||
u_lu[1] = 0;
|
||||
u = 0;
|
||||
|
||||
do {
|
||||
u++;
|
||||
if (d[u] == -1) {
|
||||
l[u + 1] = l[u];
|
||||
for (i = 0; i <= l[u]; i++) {
|
||||
elp[u + 1][i] = elp[u][i];
|
||||
elp[u][i] = index_of[elp[u][i]];
|
||||
}
|
||||
} else
|
||||
/*
|
||||
* search for words with greatest u_lu[q] for
|
||||
* which d[q]!=0
|
||||
*/
|
||||
{
|
||||
q = u - 1;
|
||||
while ((d[q] == -1) && (q > 0))
|
||||
q--;
|
||||
/* have found first non-zero d[q] */
|
||||
if (q > 0) {
|
||||
j = q;
|
||||
do {
|
||||
j--;
|
||||
if ((d[j] != -1) && (u_lu[q] < u_lu[j]))
|
||||
q = j;
|
||||
} while (j > 0);
|
||||
}
|
||||
|
||||
/*
|
||||
* have now found q such that d[u]!=0 and
|
||||
* u_lu[q] is maximum
|
||||
*/
|
||||
/* store degree of new elp polynomial */
|
||||
if (l[u] > l[q] + u - q)
|
||||
l[u + 1] = l[u];
|
||||
else
|
||||
l[u + 1] = l[q] + u - q;
|
||||
|
||||
/* form new elp(x) */
|
||||
for (i = 0; i < t2; i++)
|
||||
elp[u + 1][i] = 0;
|
||||
for (i = 0; i <= l[q]; i++)
|
||||
if (elp[q][i] != -1)
|
||||
elp[u + 1][i + u - q] =
|
||||
alpha_to[(d[u] + n - d[q] + elp[q][i]) % n];
|
||||
for (i = 0; i <= l[u]; i++) {
|
||||
elp[u + 1][i] ^= elp[u][i];
|
||||
elp[u][i] = index_of[elp[u][i]];
|
||||
}
|
||||
}
|
||||
u_lu[u + 1] = u - l[u + 1];
|
||||
|
||||
/* form (u+1)th discrepancy */
|
||||
if (u < t2) {
|
||||
/* no discrepancy computed on last iteration */
|
||||
if (s[u + 1] != -1)
|
||||
d[u + 1] = alpha_to[s[u + 1]];
|
||||
else
|
||||
d[u + 1] = 0;
|
||||
for (i = 1; i <= l[u + 1]; i++)
|
||||
if ((s[u + 1 - i] != -1) && (elp[u + 1][i] != 0))
|
||||
d[u + 1] ^= alpha_to[(s[u + 1 - i]
|
||||
+ index_of[elp[u + 1][i]]) % n];
|
||||
/* put d[u+1] into index form */
|
||||
d[u + 1] = index_of[d[u + 1]];
|
||||
}
|
||||
} while ((u < t2) && (l[u + 1] <= t));
|
||||
|
||||
u++;
|
||||
if (l[u] <= t) {/* Can correct errors */
|
||||
/* put elp into index form */
|
||||
for (i = 0; i <= l[u]; i++)
|
||||
elp[u][i] = index_of[elp[u][i]];
|
||||
|
||||
for (i = 0; i <= l[u]; i++)
|
||||
|
||||
/* Chien search: find roots of the error location polynomial */
|
||||
for (i = 1; i <= l[u]; i++)
|
||||
reg[i] = elp[u][i];
|
||||
count = 0;
|
||||
for (i = 1; i <= n; i++) {
|
||||
q = 1;
|
||||
for (j = 1; j <= l[u]; j++)
|
||||
if (reg[j] != -1) {
|
||||
reg[j] = (reg[j] + j) % n;
|
||||
q ^= alpha_to[reg[j]];
|
||||
}
|
||||
if (!q) { /* store root and error
|
||||
* location number indices */
|
||||
root[count] = i;
|
||||
loc[count] = n - i;
|
||||
count++;
|
||||
}
|
||||
}
|
||||
|
||||
if (count == l[u])
|
||||
/* no. roots = degree of elp hence <= t errors */
|
||||
for (i = 0; i < l[u]; i++)
|
||||
recd[loc[i]] ^= 1;
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
long int messagepp = 0x0;
|
||||
|
||||
//very simplified version, just to encode, get frame and compare
|
||||
unsigned long long int edacs_bch (unsigned long long int message)
|
||||
{
|
||||
int i;
|
||||
//not ideal to run these every frame, will want to eventually run once on start of DSD-FME, and store values
|
||||
read_p(); /* Read m */
|
||||
generate_gf(); /* Construct the Galois Field GF(2**m) */
|
||||
gen_poly(); /* Compute the generator polynomial of BCH code */
|
||||
//test shows this works, but output from program is that the poly is backwards, not sure if it corrects okay though
|
||||
for (i = 0; i < k; i++){
|
||||
ddata[i] = ( (message >> i) & 0x1 ); //loaded up backwards? or just outputs backwards?
|
||||
}
|
||||
|
||||
|
||||
encode_bch(); /* encode data */
|
||||
|
||||
/*
|
||||
* recd[] are the coefficients of c(x) = x**(length-k)*data(x) + b(x)
|
||||
*/
|
||||
for (i = 0; i < length - k; i++)
|
||||
recd[i] = bb[i];
|
||||
for (i = 0; i < k; i++)
|
||||
recd[i + length - k] = ddata[i];
|
||||
|
||||
for (i = 0; i < length; i++) {
|
||||
messagepp = (messagepp << 1) | recd[39-i]; //get it in the correct direction
|
||||
}
|
||||
|
||||
return (messagepp);
|
||||
|
||||
}
|
||||
|
||||
|
||||
|
|
@ -0,0 +1,418 @@
|
|||
/*-------------------------------------------------------------------------------
|
||||
* EDACS-FME
|
||||
* A program for decoding EDACS (ported to DSD-FME)
|
||||
* https://github.com/lwvmobile/edacs-fm
|
||||
*
|
||||
* Portions of this software originally from:
|
||||
* https://github.com/sp5wwp/ledacs
|
||||
* XTAL Labs
|
||||
* 30 IV 2016
|
||||
* Many thanks to SP5WWP for permission to use and modify this software
|
||||
*
|
||||
* Encoder/decoder for binary BCH codes in C (Version 3.1)
|
||||
* Robert Morelos-Zaragoza
|
||||
* 1994-7
|
||||
*
|
||||
* LWVMOBILE
|
||||
* 2022-10 Version EDACS-FM Florida Man Edition
|
||||
*-----------------------------------------------------------------------------*/
|
||||
#include "dsd.h"
|
||||
|
||||
char * getDateE(void) {
|
||||
char datename[99]; //might honestly just need a much smaller size, and not a larger one
|
||||
char * curr2;
|
||||
struct tm * to;
|
||||
time_t t;
|
||||
t = time(NULL);
|
||||
to = localtime( & t);
|
||||
strftime(datename, sizeof(datename), "%Y-%m-%d", to);
|
||||
curr2 = strtok(datename, " ");
|
||||
return curr2;
|
||||
}
|
||||
|
||||
char * getTimeE(void) //get pretty hh:mm:ss timestamp
|
||||
{
|
||||
time_t t = time(NULL);
|
||||
|
||||
char * curr;
|
||||
char * stamp = asctime(localtime( & t));
|
||||
|
||||
curr = strtok(stamp, " ");
|
||||
curr = strtok(NULL, " ");
|
||||
curr = strtok(NULL, " ");
|
||||
curr = strtok(NULL, " ");
|
||||
|
||||
return curr;
|
||||
}
|
||||
|
||||
void edacs(dsd_opts * opts, dsd_state * state)
|
||||
{
|
||||
unsigned long long fr_1 = 0xFFFFFFFFFF; //40-bit frames
|
||||
unsigned long long fr_2 = 0; //each is a 40 bit frame that repeats 3 times
|
||||
unsigned long long fr_3 = 0; //two messages per frame
|
||||
unsigned long long fr_4 = 0xFFFFFFFFFF;
|
||||
unsigned long long fr_5 = 0;
|
||||
unsigned long long fr_6 = 0;
|
||||
|
||||
//BCH stuff
|
||||
unsigned long long int fr_1m = 0xFFFFFFF; //28-bit 7X message portion to pass to bch handler
|
||||
unsigned long long int fr_1t = 0xFFFFFFFFFF; //40 bit return from BCH with poly attached
|
||||
unsigned long long int fr_4m = 0xFFFFFFF; //28-bit 7X message portion to pass to bch handler
|
||||
unsigned long long int fr_4t = 0xFFFFFFFFFF; //40 bit return from BCH with poly attached
|
||||
|
||||
unsigned char command = 0xFF;
|
||||
unsigned char mt1 = 0x1F;
|
||||
unsigned char mt2 = 0xF;
|
||||
unsigned char mta = 0;
|
||||
unsigned char lcn = 0;
|
||||
|
||||
//commands; may not use these anymore
|
||||
unsigned int vcmd = 0xEE; //voice command variable
|
||||
unsigned int idcmd = 0xFD;
|
||||
unsigned int peercmd = 0xF88; //using for EA detection test
|
||||
unsigned int netcmd = 0xF3; //using for Networked Test
|
||||
|
||||
state->edacs_vc_lcn = -1; //init on negative for ncurses and tuning
|
||||
|
||||
int i, j;
|
||||
int edacs_bit[241] = {0}; //zero out bit array and collect bits into it.
|
||||
|
||||
for (i = 0; i < 240; i++) //288 bits every transmission minus 48 bit (24 dibit) sync pattern
|
||||
{
|
||||
edacs_bit[i] = getDibit (opts, state); //getDibit returns binary 0 or 1 on GFSK signal (Edacs and PV)
|
||||
}
|
||||
|
||||
//push the edacs_bit array into fr format from EDACS-FM
|
||||
for (i = 0; i < 40; i++)
|
||||
{
|
||||
//only fr_1 and fr4 are going to matter
|
||||
fr_1 = fr_1 << 1;
|
||||
fr_1 = fr_1 | edacs_bit[i];
|
||||
fr_2 = fr_2 << 1;
|
||||
fr_2 = fr_2 | edacs_bit[i+40];
|
||||
fr_3 = fr_3 << 1;
|
||||
fr_3 = fr_3 | edacs_bit[i+80];
|
||||
|
||||
fr_4 = fr_4 << 1;
|
||||
fr_4 = fr_4 | edacs_bit[i+120];
|
||||
fr_5 = fr_5 << 1;
|
||||
fr_5 = fr_5 | edacs_bit[i+160];
|
||||
fr_6 = fr_6 << 1;
|
||||
fr_6 = fr_6 | edacs_bit[i+200];
|
||||
|
||||
}
|
||||
|
||||
fr_1 = fr_1 & 0xFFFFFFFFFF;
|
||||
fr_4 = fr_4 & 0xFFFFFFFFFF;
|
||||
|
||||
fr_1m = (fr_1 & 0xFFFFFFF000) >> 12;
|
||||
fr_4m = (fr_4 & 0xFFFFFFF000) >> 12;
|
||||
|
||||
//take our message and create a new crc for it, if it matches the old crc, then we have a good frame
|
||||
fr_1t = edacs_bch (fr_1m);
|
||||
fr_4t = edacs_bch (fr_4m);
|
||||
|
||||
fr_1t = fr_1t & 0xFFFFFFFFFF;
|
||||
fr_4t = fr_4t & 0xFFFFFFFFFF;
|
||||
|
||||
if (fr_1 != fr_1t || fr_4 != fr_4t)
|
||||
{
|
||||
fprintf (stderr, " BCH FAIL ");
|
||||
}
|
||||
else //BCH Pass, continue from here.
|
||||
{
|
||||
|
||||
//ESK on/off detection, I honestly don't remember the logic for this anymore, but it works fine
|
||||
if ( (((fr_1t & 0xF000000000) >> 36) != 0xB) && (((fr_1t & 0xF000000000) >> 36) != 0x1) && (((fr_1t & 0xFF00000000) >> 32) != 0xF3) )
|
||||
{
|
||||
//experimenting with values here, not too high, and not too low
|
||||
if ( (((fr_1t & 0xF000000000) >> 36) <= 0x8 ))
|
||||
{
|
||||
state->esk_mask = 0xA0;
|
||||
}
|
||||
//ideal value would be 5, but some other values exist that don't allow it
|
||||
if ( (((fr_1t & 0xF000000000) >> 36) > 0x8 ) )
|
||||
{
|
||||
state->esk_mask = 0x0;
|
||||
}
|
||||
}
|
||||
|
||||
//Standard/Networked Auto Detection
|
||||
//if (command == netcmd) //netcmd is F3 Unknown Command in Networked
|
||||
if ( (fr_1t >> 32 == netcmd) )
|
||||
{
|
||||
state->ea_mode = 0; //disable extended addressing mode
|
||||
}
|
||||
|
||||
//EA Auto detection //peercmd is 0xF88 peer site relay 0xFF80000000 >> 28
|
||||
if (fr_1t >> 28 == peercmd || fr_1t >> 28 == (peercmd ^ 0xA00) )
|
||||
{
|
||||
state->ea_mode = 1; //enable extended addressing mode
|
||||
}
|
||||
|
||||
//Start Extended Addressing Mode
|
||||
if (state->ea_mode == 1)
|
||||
{
|
||||
command = ((fr_1t & 0xFF00000000) >> 32) ^ state->esk_mask;
|
||||
mt1 = (command & 0xF8) >> 3;
|
||||
mt2 = (fr_1t & 0x780000000) >> 31;
|
||||
lcn = (fr_1t & 0x3E0000000) >> 29; //only valid during calls, status
|
||||
|
||||
//Site ID
|
||||
unsigned long long int site_id = 0; //we probably could just make this an int as well as the state variables
|
||||
if (mt1 == 0x1F && mt2 == 0xA)
|
||||
{
|
||||
site_id = ((fr_1 & 0x1F000) >> 12) | ((fr_1 & 0x1F000000) >> 19);
|
||||
fprintf (stderr, "%s", KYEL);
|
||||
fprintf (stderr, " Site ID [%02llX][%03lld] Extended Addressing", site_id, site_id);
|
||||
fprintf (stderr, "%s", KNRM);
|
||||
state->edacs_site_id = site_id;
|
||||
}
|
||||
//Patch Groups
|
||||
else if (mt1 == 0x1F && mt2 == 0xC)
|
||||
{
|
||||
int patch_site = ((fr_4t & 0xFF00000000) >> 32); //is site info valid, 0 for all sites? else patch only good on site listed?
|
||||
int sourcep = ((fr_1t & 0xFFFF000) >> 12);
|
||||
int targetp = ((fr_4t & 0xFFFF000) >> 12);
|
||||
fprintf (stderr, " Patch -- Site [%d] Source [%d] Target [%d] ", patch_site, sourcep, targetp);
|
||||
}
|
||||
//Adjacent Sites
|
||||
else if (mt1 == 0x1F && mt2 == 0x1)
|
||||
{
|
||||
fprintf (stderr, " Adjacent Site");
|
||||
if ( ((fr_1t & 0xFF000) >> 12) > 0 )
|
||||
{
|
||||
int adj = (fr_1t & 0xFF000) >> 12;
|
||||
int adj_l = (fr_1t & 0x1F000000) >> 24;
|
||||
fprintf (stderr, " [%02X][%03d] on CC LCN [%02d]", adj, adj, adj_l);
|
||||
}
|
||||
}
|
||||
//Control Channel LCN
|
||||
else if (mt1 == 0x1F && mt2 == 0x8)
|
||||
{
|
||||
fprintf (stderr, " Control Channel LCN");
|
||||
if (((fr_4t >> 12) & 0x1F) != 0)
|
||||
{
|
||||
state->edacs_cc_lcn = ((fr_4t >> 12) & 0x1F);
|
||||
if (state->edacs_cc_lcn > state->edacs_lcn_count && lcn < 26) //26, or 27. shouldn't matter don't think cc lcn will give a status lcn val
|
||||
{
|
||||
state->edacs_lcn_count = state->edacs_cc_lcn;
|
||||
}
|
||||
fprintf (stderr, " [%d]", state->edacs_cc_lcn);
|
||||
//set trunking cc here so we know where to come back to
|
||||
if (opts->p25_trunk == 1 && state->trunk_lcn_freq[state->edacs_cc_lcn-1] != 0)
|
||||
{
|
||||
state->p25_cc_freq = state->trunk_lcn_freq[state->edacs_cc_lcn-1]; //index starts at zero, lcn's locally here start at 1
|
||||
}
|
||||
}
|
||||
}
|
||||
//disabling kick command, data looks like its just FFFF, no actual values, can't verify accuracy
|
||||
// else if (mt1 == 0x1F && mt2 == 0xB) //KICK LISTING for EA?? Unverified, but probably observed in Unitrunker way back when.
|
||||
// {
|
||||
// int kicked = (fr_4t & 0xFFFFF000) >> 12;
|
||||
// fprintf (stderr, " FR_1 [%010llX]", fr_1t);
|
||||
// fprintf (stderr, " FR_3 [%010llX]", fr_3);
|
||||
// fprintf (stderr, " FR_4 [%010llX]", fr_4t);
|
||||
// fprintf (stderr, " FR_6 [%010llX]", fr_6);
|
||||
// fprintf (stderr, " Kick Command?");
|
||||
// }
|
||||
//Voice Call Grant Update
|
||||
// mt1 0x3 is Digital group voice call, 0x2 Group Data Channel, 0x1 TDMA call
|
||||
else if (mt1 >= 0x1 && mt1 <= 0x3)
|
||||
{
|
||||
//LCNs greater than 26 are considered status values, "Busy, Queue, Deny, etc"
|
||||
if (lcn > state->edacs_lcn_count && lcn < 26)
|
||||
{
|
||||
state->edacs_lcn_count = lcn;
|
||||
}
|
||||
|
||||
int group = (fr_1t & 0xFFFF000) >> 12;
|
||||
int source = (fr_4t & 0xFFFFF000) >> 12;
|
||||
if (group != 0) state->lasttg = group;
|
||||
if (source != 0) state->lastsrc = source;
|
||||
if (lcn != 0) state->edacs_vc_lcn = lcn;
|
||||
fprintf (stderr, "%s", KGRN);
|
||||
fprintf (stderr, " Group [%05d] Source [%08d] LCN[%02d]", group, source, lcn);
|
||||
|
||||
char mode[8]; //allow, block, digital, enc, etc
|
||||
|
||||
for (int i = 0; i < state->group_tally; i++)
|
||||
{
|
||||
if (state->group_array[i].groupNumber == group)
|
||||
{
|
||||
fprintf (stderr, " [%s]", state->group_array[i].groupName);
|
||||
strcpy (mode, state->group_array[i].groupMode);
|
||||
//fprintf (stderr, "[%s]", mode); //see if we are getting correct values
|
||||
}
|
||||
}
|
||||
|
||||
if (mt1 == 0x1) fprintf (stderr, " TDMA Call"); //never observed, wonder if any EDACS systems ever carried a TDMA signal (X2-TDMA?)
|
||||
if (mt1 == 0x2) fprintf (stderr, " Group Data Call"); //Never Seen this one before
|
||||
if (mt1 == 0x3) fprintf (stderr, " Digital Call"); //ProVoice, this is what we always get on SLERS EA
|
||||
fprintf (stderr, "%s", KNRM);
|
||||
|
||||
//this is working now with the new import setup
|
||||
if (opts->p25_trunk == 1 && (strcmp(mode, "DE") != 0) )
|
||||
{
|
||||
if (lcn < 26 && state->trunk_lcn_freq[lcn-1] != 0) //don't tune if zero (not loaded or otherwise)
|
||||
{
|
||||
//openwav file and do per call right here, should probably check as well to make sure we have a valid trunking method active (rigctl, rtl)
|
||||
if (opts->dmr_stereo_wav == 1)
|
||||
{
|
||||
sprintf (opts->wav_out_file, "./WAV/%s %s pV Site %lld TG %d SRC %d.wav", getDateE(), getTimeE(), state->edacs_site_id, group, source);
|
||||
openWavOutFile (opts, state);
|
||||
}
|
||||
|
||||
//do condition here, in future, will allow us to use tuning methods as well, or rtl_udp as well
|
||||
if (opts->use_rigctl == 1)
|
||||
{
|
||||
SetModulation(opts->rigctl_sockfd, 12500); //bw depends on system strength, but we want a wide one for now
|
||||
SetFreq(opts->rigctl_sockfd, state->trunk_lcn_freq[lcn-1]); //minus one because the lcn index starts at zero
|
||||
//probably should place these inside of the tuning function condition
|
||||
state->edacs_tuned_lcn = lcn;
|
||||
opts->p25_is_tuned = 1;
|
||||
|
||||
}
|
||||
|
||||
if (opts->audio_in_type == 3) //rtl dongle
|
||||
{
|
||||
//insert rtl udp command, make a new function for it!
|
||||
rtl_udp_tune(opts, state, state->trunk_lcn_freq[lcn-1]);
|
||||
//probably should place these inside of the tuning function condition
|
||||
state->edacs_tuned_lcn = lcn;
|
||||
opts->p25_is_tuned = 1;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
else //print frames for debug/analysis
|
||||
{
|
||||
fprintf (stderr, " FR_1 [%010llX]", fr_1t);
|
||||
fprintf (stderr, " FR_4 [%010llX]", fr_4t);
|
||||
fprintf (stderr, " Unknown Command");
|
||||
}
|
||||
|
||||
}
|
||||
//Start Standard or Networked Mode
|
||||
else if (state->ea_mode == 0)
|
||||
{
|
||||
//standard or networked
|
||||
command = ((fr_1t & 0xFF00000000) >> 32) ^ state->esk_mask;
|
||||
lcn = (fr_1t & 0xF8000000) >> 27;
|
||||
|
||||
//site ID and CC LCN
|
||||
if (command == 0xFD)
|
||||
{
|
||||
int site_id = (fr_1t & 0xFF000) >> 12;
|
||||
int cc_lcn = (fr_1t & 0x1F000000) >> 24;
|
||||
|
||||
fprintf (stderr, "%s", KYEL);
|
||||
fprintf (stderr, " Site ID [%02X][%03d] on CC LCN [%02d] Standard/Networked", site_id, site_id, cc_lcn);
|
||||
fprintf (stderr, "%s", KNRM);
|
||||
state->edacs_site_id = site_id;
|
||||
state->edacs_cc_lcn = cc_lcn;
|
||||
if (state->edacs_cc_lcn > state->edacs_lcn_count && lcn < 26)
|
||||
{
|
||||
state->edacs_lcn_count = state->edacs_cc_lcn;
|
||||
}
|
||||
}
|
||||
|
||||
//voice call assignment
|
||||
else if (command == 0xEE || command == 0xEF)
|
||||
{
|
||||
|
||||
if (lcn > state->edacs_lcn_count && lcn < 26)
|
||||
{
|
||||
state->edacs_lcn_count = lcn;
|
||||
}
|
||||
state->edacs_vc_lcn = lcn;
|
||||
//just going to use the default 4-4-3 A-FS scheme, making it user configurable would be a pain
|
||||
int afs = (fr_1t & 0x7FF000) >> 12;
|
||||
int a = afs >> 7;
|
||||
int fs = afs & 0x7F;
|
||||
int status = (fr_1t & 0xF00000000) >> 32;
|
||||
if (afs > 0) state->lastsrc = afs;
|
||||
fprintf (stderr, "%s", KGRN);
|
||||
fprintf (stderr, " AFS [0x%03X] [%02d-%03d] LCN [%02d]", afs, a, fs, lcn);
|
||||
|
||||
char mode[8]; //allow, block, digital, enc, etc
|
||||
|
||||
for (int i = 0; i < state->group_tally; i++)
|
||||
{
|
||||
if (state->group_array[i].groupNumber == afs)
|
||||
{
|
||||
fprintf (stderr, " [%s]", state->group_array[i].groupName);
|
||||
strcpy (mode, state->group_array[i].groupMode);
|
||||
}
|
||||
}
|
||||
|
||||
if (command == 0xEE)
|
||||
{
|
||||
//no handling for raw audio yet...that works properly atleast
|
||||
fprintf (stderr, " Analog");
|
||||
}
|
||||
else //Digital Call (ProVoice, hopefully not Aegis in 2022)
|
||||
{
|
||||
fprintf (stderr, " Digital");
|
||||
//this is working now with the new import setup
|
||||
if (opts->p25_trunk == 1 && (strcmp(mode, "DE") != 0) )
|
||||
{
|
||||
if (lcn < 26 && state->trunk_lcn_freq[lcn-1] != 0) //don't tune if zero (not loaded or otherwise)
|
||||
{
|
||||
//openwav file and do per call right here
|
||||
if (opts->dmr_stereo_wav == 1)
|
||||
{
|
||||
sprintf (opts->wav_out_file, "./WAV/%s %s pV Site %lld AFS %X.wav", getDateE(), getTimeE(), state->edacs_site_id, afs);
|
||||
openWavOutFile (opts, state);
|
||||
}
|
||||
|
||||
if (opts->use_rigctl == 1)
|
||||
{
|
||||
SetModulation(opts->rigctl_sockfd, 12500); //bw depends on system strength, but we want a wide one for now
|
||||
SetFreq(opts->rigctl_sockfd, state->trunk_lcn_freq[lcn-1]); //minus one because our index starts at zero
|
||||
//probably should place these inside of the tuning function condition
|
||||
state->edacs_tuned_lcn = lcn;
|
||||
opts->p25_is_tuned = 1;
|
||||
}
|
||||
|
||||
if (opts->audio_in_type == 3) //rtl dongle
|
||||
{
|
||||
//rtl udp command here
|
||||
rtl_udp_tune(opts, state, state->trunk_lcn_freq[lcn-1]);
|
||||
//probably should place these inside of the tuning function condition
|
||||
state->edacs_tuned_lcn = lcn;
|
||||
opts->p25_is_tuned = 1;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
fprintf (stderr, "%s", KNRM);
|
||||
}
|
||||
//Network Command, Not sure of how to handle, but just show frames
|
||||
else if (command == 0xF3)
|
||||
{
|
||||
fprintf (stderr, " FR_1 [%010llX]", fr_1t);
|
||||
fprintf (stderr, " FR_4 [%010llX]", fr_4t);
|
||||
fprintf (stderr, " Network Command");
|
||||
}
|
||||
|
||||
else //print frames for debug/analysis
|
||||
{
|
||||
fprintf (stderr, " FR_1 [%010llX]", fr_1t);
|
||||
fprintf (stderr, " FR_4 [%010llX]", fr_4t);
|
||||
fprintf (stderr, " Unknown Command");
|
||||
}
|
||||
|
||||
} //end Standard or Networked
|
||||
|
||||
}
|
||||
|
||||
fprintf (stderr, "\n");
|
||||
|
||||
}
|
||||
|
||||
|
||||
|
|
@ -13,10 +13,14 @@ processProVoice (dsd_opts * opts, dsd_state * state)
|
|||
char imbe7100_fr2[7][24];
|
||||
const int *w, *x;
|
||||
|
||||
if (opts->errorbars == 1)
|
||||
{
|
||||
fprintf (stderr,"VOICE ");
|
||||
}
|
||||
fprintf (stderr," VOICE");
|
||||
if (opts->p25_trunk == 1 && opts->p25_is_tuned == 1)
|
||||
{
|
||||
fprintf (stderr, "%s", KGRN);
|
||||
fprintf (stderr, " Site [%02llX][%03lld] Group [%05d] Source [%08d] LCN[%02d] ",
|
||||
state->edacs_site_id, state->edacs_site_id, state->lasttg, state->lastsrc, state->edacs_tuned_lcn);
|
||||
fprintf (stderr, "%s", KNRM);
|
||||
}
|
||||
|
||||
for (i = 0; i < 64; i++)
|
||||
{
|
||||
|
|
|
|||
|
|
@ -959,7 +959,7 @@ static void *socket_thread_fn(void *arg) {
|
|||
rtlsdr_set_center_freq(dongle.dev, dongle.freq);
|
||||
//verbose_set_frequency (dongle.dev, new_freq);
|
||||
//fprintf (stderr, "Tuning to: %d [Hz] (central freq: %d [Hz])\n", new_freq, new_freq + freq_offset);
|
||||
fprintf (stderr, "Tuning to: %d [Hz] \n", new_freq);
|
||||
fprintf (stderr, "\nTuning to: %d [Hz] \n", new_freq);
|
||||
}
|
||||
/*
|
||||
if(buffer[0] == 1) {
|
||||
|
|
|
|||
Loading…
Reference in New Issue