From 5ecbce6bdd69e270896a1caf4dac13daee7fa954 Mon Sep 17 00:00:00 2001 From: tadscottsmith <78445808+tadscottsmith@users.noreply.github.com> Date: Tue, 3 Sep 2024 21:00:21 -0500 Subject: [PATCH] Add P25 custom frequency table CSV file parsing. (#968) --- docs/CONFIGURE.md | 26 +++++++++++ trunk-recorder/config.cc | 10 ++++ trunk-recorder/systems/p25_parser.cc | 66 +++++++++++++++++++++++++++ trunk-recorder/systems/p25_parser.h | 5 ++ trunk-recorder/systems/system.h | 3 ++ trunk-recorder/systems/system_impl.cc | 16 +++++++ trunk-recorder/systems/system_impl.h | 4 ++ 7 files changed, 130 insertions(+) diff --git a/docs/CONFIGURE.md b/docs/CONFIGURE.md index 2c7c56783..c07c9f143 100644 --- a/docs/CONFIGURE.md +++ b/docs/CONFIGURE.md @@ -248,6 +248,7 @@ There is a list of available Plugins [here](./Plugins.md). | bandplanHigh | | | number | *SmartNet, 400_custom only* The highest channel in the system, specified in Hz. | | bandplanSpacing | | | number | *SmartNet, 400_custom only* The channel spacing, specified in Hz. Typically this is *25000*. | | bandplanOffset | | | number | *SmartNet, 400_custom only* The offset used to calculate frequencies. | +| customFrequencyTableFile| | | string | *P25 only* The filename for a CSV file that provides information about the P25 custom frequency tables. The format for the file is described below. | | decodeMDC | | false | **true** / **false** | *Conventional systems only* enable the MDC-1200 signaling decoder. | | decodeFSync | | false | **true** / **false** | *Conventional systems only* enable the Fleet Sync signaling decoder. | | decodeStar | | false | **true** / **false** | *Conventional systems only* enable the Star signaling decoder. | @@ -534,3 +535,28 @@ In the second row of the example below, the first capture group `([0-9]{2})` bec | 911000 | Dispatch | | 1[1245]10([0-9]{2})[127] | Engine $1 | | /^1[78]3(1[0-9]{2})/ | Ambulance $1 | + +## customFrequencyTableFile + +This file allows for you to specify custom P25 frequency table information. + +**It is highly recommended to only use this file when the system control channel is not accurately broadcasting frequency table information. In most cases, this file should not be needed.** + +| Column Name | Required | Value | +|-------------|----------|-------| +| TABLEID | ✔️ | The frequency table ID. This ID uses One-Based numbering to match the RadioReference format. | +| TYPE | ✔️ | The type of frequency table. This should be either **TDMA** or **FDMA**. | +| BASE | ✔️ | The base frequency defined in MHz. (Example: 851.00625)| +| SPACING | ✔️ | The channel spacing defined in KHz. (Example 6.25) | +| OFFSET | ✔️ | The transmit offset defined in MHz. (Example -45) | + +A **Header Row** is required for the file and the headers must match the column names above. Column headers are case sensitive and must be provided in uppercase. + +**RadioReference Subscribers** please note that if you copy this information directly from RadioReference, you will need to update the column headers. + +| TABLEID | TYPE | BASE | SPACING | OFFSET | +|---------|------|-----------|---------|--------| +| 1 | FDMA | 851.00625 | 6.25 | -45 | +| 2 | FDMA | 762.00625 | 6.25 | +30 | +| 3 | TDMA | 851.01250 | 12.5 | -45 | +| 4 | TDMA | 762.00625 | 12.5 | +30 | \ No newline at end of file diff --git a/trunk-recorder/config.cc b/trunk-recorder/config.cc index 233d71325..faae47ff2 100644 --- a/trunk-recorder/config.cc +++ b/trunk-recorder/config.cc @@ -284,6 +284,16 @@ bool load_config(string config_file, Config &config, gr::top_block_sptr &tb, std } system->set_talkgroups_file(element.value("talkgroupsFile", "")); BOOST_LOG_TRIVIAL(info) << "Talkgroups File: " << system->get_talkgroups_file(); + + bool custom_freq_table_file_exists = element.contains("customFrequencyTableFile"); + if (custom_freq_table_file_exists) + { + std::string custom_freq_table_file = element["customFrequencyTableFile"]; + system->set_custom_freq_table_file(custom_freq_table_file); + BOOST_LOG_TRIVIAL(info) << "Custom Frequency Table File: " << custom_freq_table_file; + } + + } else { BOOST_LOG_TRIVIAL(error) << "System Type in config.json not recognized"; return false; diff --git a/trunk-recorder/systems/p25_parser.cc b/trunk-recorder/systems/p25_parser.cc index a345b3926..6a9275010 100644 --- a/trunk-recorder/systems/p25_parser.cc +++ b/trunk-recorder/systems/p25_parser.cc @@ -1,8 +1,69 @@ #include "p25_parser.h" #include "../formatter.h" +using namespace csv; + P25Parser::P25Parser() {} + +void P25Parser::load_freq_table(std::string custom_freq_table_file, int sys_num) { + + if (custom_freq_table_file == "") { + return; + } else { + BOOST_LOG_TRIVIAL(info) << "Loading Custom Frequency Table File: " << custom_freq_table_file; + } + + CSVFormat format; + format.trim({' ', '\t'}); + CSVReader reader(custom_freq_table_file, format); + std::vector headers = reader.get_col_names(); + + if (headers[0] != "TABLEID" || headers[1] != "TYPE" || headers[2] != "BASE" || headers[3] != "SPACING" || headers[4] != "OFFSET" ) { + BOOST_LOG_TRIVIAL(error) << "Cuustom Frequency Table File Invalid Headers."; + } + + for (CSVRow &row : reader) { // Input iterator + unsigned long id = row["TABLEID"].get() - 1; //CSV Format is One-Based, but the Standard is Zero-Based. + std::string type = row["TYPE"].get(); + unsigned long frequency = row["BASE"].get() * 1000000; + unsigned long step = row["SPACING"].get() * 1000; + + // Need to remove the "+" sign from positive numbers if it exists. + std::string offset_string = row["OFFSET"].get(); + if(offset_string[0] == '+'){ + offset_string = offset_string.substr(1); + } + + long offset = std::stol(offset_string) * 1000000; + + Freq_Table temp_table; + temp_table.id = id; + temp_table.frequency = frequency; + temp_table.step = step; + temp_table.offset = offset; + + if(type == "FDMA"){ + temp_table.phase2_tdma = false; + temp_table.slots_per_carrier = 1; + temp_table.bandwidth = 12.5; + } + else + { + temp_table.phase2_tdma = true; + temp_table.slots_per_carrier = 2; + temp_table.bandwidth = 6.25; + } + + BOOST_LOG_TRIVIAL(info) << "Adding Frequency Table:\t" << id << "\t" << type << "\t" << frequency << "\t" << step << "\t" << offset; + + add_freq_table(id, temp_table, sys_num); + } + + custom_freq_table_loaded = true; + +} + void P25Parser::add_freq_table(int freq_table_id, Freq_Table temp_table, int sys_num) { /*std::cout << "Add - Channel id " << std::dec << chan_id << " freq " << temp_table.frequency << " offset " << temp_table.offset << " step " << @@ -962,6 +1023,11 @@ std::vector P25Parser::parse_message(gr::message::sptr msg, System long type = msg->type(); int sys_num = system->get_sys_num(); + + if(system->has_custom_freq_table_file() && custom_freq_table_loaded == false){ + load_freq_table(system->get_custom_freq_table_file(), sys_num); + } + TrunkMessage message; message.message_type = UNKNOWN; message.opcode = 255; diff --git a/trunk-recorder/systems/p25_parser.h b/trunk-recorder/systems/p25_parser.h index 3b526965b..ee60e3d92 100644 --- a/trunk-recorder/systems/p25_parser.h +++ b/trunk-recorder/systems/p25_parser.h @@ -12,6 +12,9 @@ #include #include +#include "../csv_helper.h" +#include + struct Freq_Table { unsigned long id; long offset; @@ -25,6 +28,7 @@ struct Freq_Table { class P25Parser : public TrunkParser { std::map> freq_tables; std::map::iterator it; + bool custom_freq_table_loaded = false; public: P25Parser(); @@ -37,6 +41,7 @@ class P25Parser : public TrunkParser { std::string channel_id_to_freq_string(int chan_id, int sys_num); void print_bitset(boost::dynamic_bitset<> &tsbk); void add_freq_table(int freq_table_id, Freq_Table table, int sys_num); + void load_freq_table(std::string custom_freq_table_file, int sys_num); double channel_id_to_frequency(int chan_id, int sys_num); std::string channel_to_string(int chan, int sys_num); std::vector parse_message(gr::message::sptr msg, System *system); diff --git a/trunk-recorder/systems/system.h b/trunk-recorder/systems/system.h index 79a2f1e0d..ff7968110 100644 --- a/trunk-recorder/systems/system.h +++ b/trunk-recorder/systems/system.h @@ -121,6 +121,9 @@ class System { virtual void set_channel_file(std::string channel_file) = 0; virtual bool has_channel_file() = 0; virtual void set_unit_tags_file(std::string) = 0; + virtual void set_custom_freq_table_file(std::string custom_freq_table_file) = 0; + virtual std::string get_custom_freq_table_file() = 0; + virtual bool has_custom_freq_table_file() = 0; virtual int control_channel_count() = 0; virtual void add_control_channel(double channel) = 0; virtual double get_next_control_channel() = 0; diff --git a/trunk-recorder/systems/system_impl.cc b/trunk-recorder/systems/system_impl.cc index e36fc34a6..a7a4890cd 100644 --- a/trunk-recorder/systems/system_impl.cc +++ b/trunk-recorder/systems/system_impl.cc @@ -323,6 +323,22 @@ void System_impl::set_unit_tags_file(std::string unit_tags_file) { this->unit_tags->load_unit_tags(unit_tags_file); } +void System_impl::set_custom_freq_table_file(std::string custom_freq_table_file) { + this->custom_freq_table_file = custom_freq_table_file; +} + +std::string System_impl::get_custom_freq_table_file(){ + return this->custom_freq_table_file; +} + +bool System_impl::has_custom_freq_table_file() { + if (this->custom_freq_table_file.length() > 0) { + return true; + } else { + return false; + } +} + Source *System_impl::get_source() { return this->source; } diff --git a/trunk-recorder/systems/system_impl.h b/trunk-recorder/systems/system_impl.h index 3dcb4106f..81d810157 100644 --- a/trunk-recorder/systems/system_impl.h +++ b/trunk-recorder/systems/system_impl.h @@ -59,6 +59,7 @@ class System_impl : public System { std::string talkgroups_file; std::string channel_file; std::string unit_tags_file; + std::string custom_freq_table_file; std::string short_name; std::string api_key; std::string bcfy_api_key; @@ -181,6 +182,9 @@ class System_impl : public System { void set_channel_file(std::string channel_file); bool has_channel_file(); void set_unit_tags_file(std::string); + void set_custom_freq_table_file(std::string custom_freq_table_file); + std::string get_custom_freq_table_file(); + bool has_custom_freq_table_file(); int control_channel_count(); int get_message_count(); void set_message_count(int count);