Skip to content

Commit

Permalink
Add P25 custom frequency table CSV file parsing. (#968)
Browse files Browse the repository at this point in the history
  • Loading branch information
tadscottsmith authored Sep 4, 2024
1 parent f00a05e commit 5ecbce6
Show file tree
Hide file tree
Showing 7 changed files with 130 additions and 0 deletions.
26 changes: 26 additions & 0 deletions docs/CONFIGURE.md
Original file line number Diff line number Diff line change
Expand Up @@ -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. |
Expand Down Expand Up @@ -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 |
10 changes: 10 additions & 0 deletions trunk-recorder/config.cc
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down
66 changes: 66 additions & 0 deletions trunk-recorder/systems/p25_parser.cc
Original file line number Diff line number Diff line change
@@ -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<std::string> 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<unsigned long>() - 1; //CSV Format is One-Based, but the Standard is Zero-Based.
std::string type = row["TYPE"].get<std::string>();
unsigned long frequency = row["BASE"].get<double>() * 1000000;
unsigned long step = row["SPACING"].get<double>() * 1000;

// Need to remove the "+" sign from positive numbers if it exists.
std::string offset_string = row["OFFSET"].get<std::string>();
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 " <<
Expand Down Expand Up @@ -962,6 +1023,11 @@ std::vector<TrunkMessage> 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;
Expand Down
5 changes: 5 additions & 0 deletions trunk-recorder/systems/p25_parser.h
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,9 @@
#include <map>
#include <vector>

#include "../csv_helper.h"
#include <csv-parser/csv.hpp>

struct Freq_Table {
unsigned long id;
long offset;
Expand All @@ -25,6 +28,7 @@ struct Freq_Table {
class P25Parser : public TrunkParser {
std::map<int, std::map<int, Freq_Table>> freq_tables;
std::map<int, Freq_Table>::iterator it;
bool custom_freq_table_loaded = false;

public:
P25Parser();
Expand All @@ -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<TrunkMessage> parse_message(gr::message::sptr msg, System *system);
Expand Down
3 changes: 3 additions & 0 deletions trunk-recorder/systems/system.h
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down
16 changes: 16 additions & 0 deletions trunk-recorder/systems/system_impl.cc
Original file line number Diff line number Diff line change
Expand Up @@ -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;
}
Expand Down
4 changes: 4 additions & 0 deletions trunk-recorder/systems/system_impl.h
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down Expand Up @@ -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);
Expand Down

0 comments on commit 5ecbce6

Please sign in to comment.