From 66f710e877ec504be5b00e3367b54be91a8829ee Mon Sep 17 00:00:00 2001 From: lwvmobile Date: Wed, 23 Nov 2022 09:08:32 -0500 Subject: [PATCH] Channel to Frequency Map CSV Importer; --- .gitignore | 1 - README.md | 2 + examples/channel_map.csv | 4 ++ include/dsd.h | 13 +++---- src/dsd_import.c | 51 +++++++++++++++++++++++- src/dsd_main.c | 20 ++++++++-- src/nxdn_element.c | 10 ++--- src/p25_frequency.c | 83 +++++++++++++++++++++++++--------------- 8 files changed, 134 insertions(+), 50 deletions(-) create mode 100644 examples/channel_map.csv diff --git a/.gitignore b/.gitignore index 2a09268..f6d5e69 100644 --- a/.gitignore +++ b/.gitignore @@ -3,4 +3,3 @@ *.dylib build *.sh -*.csv \ No newline at end of file diff --git a/README.md b/README.md index bc0c5be..93efddc 100644 --- a/README.md +++ b/README.md @@ -50,6 +50,8 @@ sudo make install -----command line switches here subject to change------------------ -1 Import LCN Frequencies from csv file (numeral 'one') (See lcn.csv for example) + -7 Import Channel to Frequency Map from csv file (numeral 'seven') + (See channel_map.csv for example) -2 Import Group List Allow/Block and Label from csv file (numeral 'two') (See group.csv for example) -3 Enable Experimental Trunking Features (P25/EDACS/NXDN-ish for now) with RIGCTL/TCP or RTL Input diff --git a/examples/channel_map.csv b/examples/channel_map.csv new file mode 100644 index 0000000..6fbfdc9 --- /dev/null +++ b/examples/channel_map.csv @@ -0,0 +1,4 @@ +ChannelNumber(dec),frequency(Hz) +1,851375000 +800,154987500 +801,451375000 diff --git a/include/dsd.h b/include/dsd.h index 937939d..8733bc4 100644 --- a/include/dsd.h +++ b/include/dsd.h @@ -364,6 +364,7 @@ typedef struct //csv import filenames char group_in_file[1024]; char lcn_in_file[1024]; + char chan_in_file[1024]; //end import filenames } dsd_opts; @@ -632,6 +633,7 @@ typedef struct //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 + long int trunk_chan_map[0xFFFF]; //NXDN - 10 bit; P25 - 16 bit; DMR up to 12 bit (standard TIII) groupinfo group_array[0x3FF]; //max supported by Cygwin is 3FFF, I hope nobody actually tries to import this many groups unsigned int group_tally; //tally number of groups imported from CSV file for referencing later int lcn_freq_count; @@ -830,14 +832,8 @@ void nxdn_deperm_facch2_udch (dsd_opts * opts, dsd_state * state, uint8_t bits[3 void nxdn_message_type (dsd_opts * opts, dsd_state * state, uint8_t MessageType); void nxdn_voice (dsd_opts * opts, dsd_state * state, int voice, uint8_t dbuf[182]); -//utility functions from OP25 needed by NXDN frame handler +//OP25 NXDN CRC functions static inline int load_i(const uint8_t val[], int len); -static inline void cfill(uint8_t result[], const uint8_t src[], int len); - -static inline void trellis_encode(uint8_t result[], const uint8_t source[], int result_len, int reg); -static inline void trellis_decode(uint8_t result[], const uint8_t source[], int result_len); - -//OP25 CRC functions static uint8_t crc6(const uint8_t buf[], int len); static uint16_t crc12f(const uint8_t buf[], int len); static uint16_t crc15(const uint8_t buf[], int len); @@ -969,7 +965,7 @@ void process_FACCH_MAC_PDU (dsd_opts * opts, dsd_state * state, int payload[156] long int process_channel_to_freq (dsd_opts * opts, dsd_state * state, int channel); //NXDN Channel to Frequency, Courtesy of IcomIcR20 on RR Forums -long int nxdn_channel_to_frequency (dsd_opts * opts, uint16_t channel); +long int nxdn_channel_to_frequency (dsd_opts * opts, dsd_state * state, uint16_t channel); //rigctl functions and TCP/UDP functions void error(char *msg); @@ -1000,6 +996,7 @@ 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); +int csvChanImport(dsd_opts * opts, dsd_state * state); #ifdef __cplusplus extern "C" { diff --git a/src/dsd_import.c b/src/dsd_import.c index 694d797..1802502 100644 --- a/src/dsd_import.c +++ b/src/dsd_import.c @@ -12,7 +12,7 @@ int csvGroupImport(dsd_opts * opts, dsd_state * state) FILE * fp; fp = fopen(filename, "r"); if (fp == NULL) { - printf("Unable to open file '%s'\n", filename); + printf("Unable to open group file '%s'\n", filename); exit(1); } int row_count = 0; @@ -64,7 +64,7 @@ int csvLCNImport(dsd_opts * opts, dsd_state * state) //LCN/LSN import for EDACS/ FILE * fp; fp = fopen(filename, "r"); if (fp == NULL) { - printf("Unable to open file '%s'\n", filename); + printf("Unable to open lcn file '%s'\n", filename); //have this return -1 and handle it inside of main exit(1); } @@ -94,3 +94,50 @@ int csvLCNImport(dsd_opts * opts, dsd_state * state) //LCN/LSN import for EDACS/ fclose(fp); return 0; } + +int csvChanImport(dsd_opts * opts, dsd_state * state) //channel map import +{ + char filename[1024] = "filename.csv"; + sprintf (filename, "%s", opts->chan_in_file); + + char buffer[BSIZE]; + FILE * fp; + fp = fopen(filename, "r"); + if (fp == NULL) { + printf("Unable to open channel map file '%s'\n", filename); + //have this return -1 and handle it inside of main + exit(1); + } + int row_count = 0; + int field_count = 0; + + long int chan_number; + + 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) + { + sscanf (field, "%ld", &chan_number); + } + + if (field_count == 1) + { + sscanf (field, "%ld", &state->trunk_chan_map[chan_number]); + } + + field = strtok(NULL, ","); + field_count++; + } + fprintf (stderr, "Channel [%05ld] [%09ld]", chan_number, state->trunk_chan_map[chan_number]); + fprintf (stderr, "\n"); + + } + fclose(fp); + return 0; +} diff --git a/src/dsd_main.c b/src/dsd_main.c index d6df834..8aa114f 100644 --- a/src/dsd_main.c +++ b/src/dsd_main.c @@ -307,6 +307,11 @@ initOpts (dsd_opts * opts) opts->wav_out_file_raw[0] = 0; opts->symbol_out_file[0] = 0; opts->lrrp_out_file[0] = 0; + //csv import filenames + opts->group_in_file[0] = 0; + opts->lcn_in_file[0] = 0; + opts->chan_in_file[0] = 0; + //end import filenames opts->szNumbers[0] = 0; opts->symbol_out_f = NULL; opts->symbol_out = 0; @@ -699,6 +704,7 @@ initState (dsd_state * state) //trunking memset (state->trunk_lcn_freq, 0, sizeof(state->trunk_lcn_freq)); + memset (state->trunk_chan_map, 0, sizeof(state->trunk_chan_map)); 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? @@ -850,6 +856,8 @@ usage () printf (" Experimental Functions and Features---------------------------------------------------\n"); printf (" -1 Import LCN Frequencies from csv file (numeral 'one') \n"); printf (" (See lcn.csv for example)\n"); + printf (" -7 Import Channel to Frequency Map from csv file (numeral 'seven') \n"); + printf (" (See channel_map.csv for example)\n"); printf (" -2 Import Group List Allow/Block and Label from csv file (numeral 'two')\n"); printf (" (See group.csv for example)\n"); printf (" -3 Enable Extremely Experimental Trunking Features (NXDN/P25/EDACS for now) with RIGCTL/TCP or RTL Input\n"); @@ -1086,7 +1094,7 @@ main (int argc, char **argv) exitflag = 0; signal (SIGINT, sigfun); - 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:6:")) != -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:6:7:")) != -1) { opterr = 0; switch (c) @@ -1100,13 +1108,19 @@ main (int argc, char **argv) opts.call_alert = 1; break; //placeholder until letters get re-arranged (or opt_long switched in) - case '1': //LCN/Frequency CSV + case '1': //LCN to Frequency CSV Import 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 + case '7': //Channel Map Frequency CSV Import + strncpy(opts.chan_in_file, optarg, 1023); + opts.chan_in_file[1023] = '\0'; + csvChanImport (&opts, &state); + break; + //placeholder until letters get re-arranged + case '2': //Group CSV Import strncpy(opts.group_in_file, optarg, 1023); opts.group_in_file[1023] = '\0'; csvGroupImport(&opts, &state); diff --git a/src/nxdn_element.c b/src/nxdn_element.c index 101d745..ed507e7 100644 --- a/src/nxdn_element.c +++ b/src/nxdn_element.c @@ -231,7 +231,7 @@ void NXDN_decode_VCALL_ASSGN(dsd_opts * opts, dsd_state * state, uint8_t * Messa //run process to figure out frequency value from the channel, or from lcn import array long int freq = 0; - freq = nxdn_channel_to_frequency(opts, Channel); + freq = nxdn_channel_to_frequency(opts, state, Channel); //run group/source analysis and tune if available/desired //group list mode so we can look and see if we need to block tuning any groups, etc @@ -373,8 +373,8 @@ void NXDN_decode_cch_info(dsd_opts * opts, dsd_state * state, uint8_t * Message) else if (channel1sts & 0x10) fprintf (stderr, "New "); else if (channel1sts & 0x8) fprintf (stderr, "Candidate Added "); else if (channel1sts & 0x4) fprintf (stderr, "Candidate Deleted "); - freq1 = nxdn_channel_to_frequency (opts, channel1); - freq2 = nxdn_channel_to_frequency (opts, channel2); + freq1 = nxdn_channel_to_frequency (opts, state, channel1); + freq2 = nxdn_channel_to_frequency (opts, state, channel2); //add handling for adding (or deleting?) frequencies to CC list if (channel1sts & 0x20 || channel1sts & 0x8) //current or new only @@ -497,12 +497,12 @@ void NXDN_decode_site_info(dsd_opts * opts, dsd_state * state, uint8_t * Message if (channel1 != 0) { fprintf (stderr, "\n Control Channel 1 [%03X][%04d] ", channel1, channel1 ); - freq1 = nxdn_channel_to_frequency (opts, channel1); + freq1 = nxdn_channel_to_frequency (opts, state, channel1); } if (channel2 != 0) { fprintf (stderr, "\n Control Channel 2 [%03X][%04d] ", channel2, channel2 ); - freq2 = nxdn_channel_to_frequency (opts, channel2); + freq2 = nxdn_channel_to_frequency (opts, state, channel2); } fprintf (stderr, "%s", KNRM); diff --git a/src/p25_frequency.c b/src/p25_frequency.c index 0c3d177..90b6c4d 100644 --- a/src/p25_frequency.c +++ b/src/p25_frequency.c @@ -31,23 +31,36 @@ long int process_channel_to_freq (dsd_opts * opts, dsd_state * state, int channe int type = state->p25_chan_type[iden]; int slots_per_carrier[16] = {1,1,1,2,4,2,2,2,2,2,2,2,2,2,2,2}; //from OP25 int step = (channel & 0xFFF) / slots_per_carrier[type]; - - if (state->p25_base_freq[iden] != 0) + + //first, check channel map + if (state->trunk_chan_map[channel] != 0) { - freq = (state->p25_base_freq[iden] * 5) + ( step * state->p25_chan_spac[iden] * 125); - fprintf (stderr, "\n Frequency [%.6lf] MHz", (double)freq/1000000); - return (freq); + freq = state->trunk_chan_map[channel]; + fprintf (stderr, "\n Frequency [%.6lf] MHz", (double)freq/1000000); + return (freq); } - else - { - fprintf (stderr, "\n Base Frequency Not Found - Iden [%d]", iden); - return (0); - } + + //if not found, attempt to find it via calculation + else + { + if (state->p25_base_freq[iden] != 0) + { + freq = (state->p25_base_freq[iden] * 5) + ( step * state->p25_chan_spac[iden] * 125); + fprintf (stderr, "\n Frequency [%.6lf] MHz", (double)freq/1000000); + return (freq); + } + else + { + fprintf (stderr, "\n Base Frequency Not Found - Iden [%d]", iden); + fprintf(stderr, "\n or Channel not found in import file"); + return (0); + } + } } //NXDN Channel to Frequency, Courtesy of IcomIcR20 on RR Forums -long int nxdn_channel_to_frequency(dsd_opts * opts, uint16_t channel) +long int nxdn_channel_to_frequency(dsd_opts * opts, dsd_state * state, uint16_t channel) { long int freq; long int base = 0; @@ -55,33 +68,41 @@ long int nxdn_channel_to_frequency(dsd_opts * opts, uint16_t channel) //the base freq value is per EricCottrell in the Understanding NXDN Trunking Forum Thread //will need to do some research or tests to confirm these values are accurate - if ((channel > 0) && (channel <= 400)) + //first, check channel map + if (state->trunk_chan_map[channel] != 0) { - if (opts->frame_nxdn48 == 1) base = 450000000; - else base = 451000000; - - freq = base + (channel - 1) * 12500; + freq = state->trunk_chan_map[channel]; fprintf (stderr, "\n Frequency [%.6lf] MHz", (double)freq/1000000); return (freq); } - else if ((channel >= 401) && (channel <= 800)) - { - if (opts->frame_nxdn48 == 1) base = 460000000; - else base = 461000000; - freq = base + (channel - 401) * 12500; - fprintf (stderr, "\n Frequency [%.6lf] MHz", (double)freq/1000000); - return (freq); - } - else if (channel > 800) - { - //write function to check the LCN import array for custom frequency values! - //return (freq); - } + //if not found, attempt to find it via calculation else { - fprintf(stderr, "\n This LCN is used for a non-standard frequency. No frequency can be reported"); - return (0); + if ((channel > 0) && (channel <= 400)) + { + if (opts->frame_nxdn48 == 1) base = 450000000; + else base = 451000000; + + freq = base + (channel - 1) * 12500; + fprintf (stderr, "\n Frequency [%.6lf] MHz", (double)freq/1000000); + return (freq); + } + else if ((channel >= 401) && (channel <= 800)) + { + if (opts->frame_nxdn48 == 1) base = 460000000; + else base = 461000000; + + freq = base + (channel - 401) * 12500; + fprintf (stderr, "\n Frequency [%.6lf] MHz", (double)freq/1000000); + return (freq); + } + else + { + fprintf(stderr, "\n Non-standard frequency or channel not found in import file"); + return (0); + } } + } \ No newline at end of file