基于C++的业余无线电呼号查询工具
本帖最后由 bgwu666 于 2024-9-30 22:30 编辑## 前言
自从接触业余无线电后,就会对见到的每个呼号都产生好奇心,总想查一查这些HAM呼号的QTH。网络上的QTH查询工具需要呼号所有者自行上传位置,而没上传的自然就查不到,于是萌生了自己写一个查询工具的想法,查到具体省份即可。
该工具依据*业余无线电台呼号管理办法*,基于C++所写,为初学练手项目,如有不足,恳请批评指正:(https://github.com/Goal75KG/CallSignQuery)。
## 功能
- 根据呼号解析出国家信息(暂时只能解析中国)
- 查询电台种类(个人业余电台、信标台、卫星电台等)
- 确定呼号所属省份
- 处理特殊情况的电台种类
## 示例
输入呼号:`BA8MAA`
输出结果:
```
呼号: BA8MAA
国家: 中国
电台种类: 个人业余电台(一级)
省份: 贵州
```
## 实现过程
**映射呼号每部分规则**
```
// 加载国家前缀规则
std::map<std::string, std::string> loadCountryPrefixRules() {
std::map<std::string, std::string> countryPrefixRules;
countryPrefixRules["B"] = "中国";
return countryPrefixRules;
// 加载电台种类规则
std::map<char, std::string> loadStationTypeRules() {
std::map<char, std::string> stationTypeRules;
stationTypeRules['A'] = "个人业余电台(一级)";
stationTypeRules['D'] = "个人业余电台(二级)";
stationTypeRules['E'] = "个人业余电台(二级)";
stationTypeRules['G'] = "个人业余电台(三级、四级、五级(收信台))";
stationTypeRules['H'] = "个人业余电台(三级、四级、五级(收信台))";
stationTypeRules['I'] = "个人业余电台(三级、四级、五级(收信台))";
stationTypeRules['J'] = "业余信标台";
stationTypeRules['L'] = "外籍及香港、澳门、台湾地区人员在中国内地设置的业余电台";
stationTypeRules['R'] = "业余中继台";
stationTypeRules['T'] = "特设业余电台";
stationTypeRules['Y'] = "集体业余电台";
return stationTypeRules;
}
// 加载电台种类规则
std::map<char, std::string> loadStationTypeRules() {
std::map<char, std::string> stationTypeRules;
stationTypeRules['A'] = "个人业余电台(一级)";
stationTypeRules['D'] = "个人业余电台(二级)";
stationTypeRules['E'] = "个人业余电台(二级)";
stationTypeRules['G'] = "个人业余电台(三级、四级、五级(收信台))";
stationTypeRules['H'] = "个人业余电台(三级、四级、五级(收信台))";
stationTypeRules['I'] = "个人业余电台(三级、四级、五级(收信台))";
stationTypeRules['J'] = "业余信标台";
stationTypeRules['L'] = "外籍及香港、澳门、台湾地区人员在中国内地设置的业余电台";
stationTypeRules['R'] = "业余中继台";
stationTypeRules['T'] = "特设业余电台";
stationTypeRules['Y'] = "集体业余电台";
return stationTypeRules;
}
```
**判断对应后缀是否在范围内**
```
bool isWithinRange(const std::string &suffix, const std::string &rangeStart, const std::string &rangeEnd) {
return suffix >= rangeStart && suffix <= rangeEnd;
}
```
**呼号匹配**
```
void queryCallSign(const std::string &callSign) {
// 加载各类规则
auto countryRules = loadCountryPrefixRules();
auto stationTypeRules = loadStationTypeRules();
auto regionProvinceRules = loadRegionProvinceRules();
// 确保呼号至少有5位
if (callSign.length() < 5) {
std::cout << "呼号格式不正确" << std::endl;
return;
}
// 解析国家前缀
std::string country = countryRules;
// 解析电台种类
char stationType = callSign;
std::string station = stationTypeRules;
// 解析地区分区
char region = callSign;
// 解析后缀
std::string suffix = callSign.substr(3, 2); // 假设后缀长度为2位
// 查找该地区的省份范围
std::string provinceName = "未知省份";
if (regionProvinceRules.find(region) != regionProvinceRules.end()) {
for (const auto &province: regionProvinceRules) {
if (isWithinRange(suffix, province.second.first, province.second.second)) {
provinceName = province.first;
break;
}
}
}
```
**处理特殊情况(业余信标台、卫星业余电台、特设业余电台)**
```
if (stationType == 'J') {
char suffix1 = callSign;
if (suffix1 == 'H' || suffix1 == 'V' || suffix1 == 'U') {
station = "HF、VHF、UHV频段信标台";
} else if (suffix1 == 'D') {
station = "业余无线电测向专用信标台";
} else if (suffix1 == 'S' && region == '1') {
station = "卫星业余电台";
} else if (suffix1 == 'T') {
station = "特设业余电台";
}
}
```
**结果输出**
```
if (!country.empty() && !station.empty() && provinceName != "未知省份") {
std::cout << "呼号: " << callSign << std::endl;
std::cout << "国家: " << country << std::endl;
std::cout << "电台种类: " << station << std::endl;
std::cout << "省份: " << provinceName << std::endl;
} else {
std::cout << "未找到呼号对应的信息" << std::endl;
}
}
```
**主程序**
```
int main() {
std::string callSign;
std::cout << "请输入要查询的呼号: ";
std::cin >> callSign;
std::cout << "\n查询结果如下:\n" << std::endl;
// 查询呼号信息
queryCallSign(callSign);
system("pause");
return 0;
}
```
## 源代码
```
#include <iostream>
#include <map>
#include <string>
// 加载国家前缀规则
std::map<std::string, std::string> loadCountryPrefixRules() {
std::map<std::string, std::string> countryPrefixRules;
countryPrefixRules["B"] = "中国";
return countryPrefixRules;
}
// 加载电台种类规则
std::map<char, std::string> loadStationTypeRules() {
std::map<char, std::string> stationTypeRules;
stationTypeRules['A'] = "个人业余电台(一级)";
stationTypeRules['D'] = "个人业余电台(二级)";
stationTypeRules['E'] = "个人业余电台(二级)";
stationTypeRules['G'] = "个人业余电台(三级、四级、五级(收信台))";
stationTypeRules['H'] = "个人业余电台(三级、四级、五级(收信台))";
stationTypeRules['I'] = "个人业余电台(三级、四级、五级(收信台))";
stationTypeRules['J'] = "业余信标台";
stationTypeRules['L'] = "外籍及香港、澳门、台湾地区人员在中国内地设置的业余电台";
stationTypeRules['R'] = "业余中继台";
stationTypeRules['T'] = "特设业余电台";
stationTypeRules['Y'] = "集体业余电台";
return stationTypeRules;
}
// 加载大区和省份范围规则
std::map<char, std::map<std::string, std::pair<std::string, std::string> > > loadRegionProvinceRules() {
std::map<char, std::map<std::string, std::pair<std::string, std::string> > > regionProvinceRules;
// 第1区:北京
regionProvinceRules['1'] = {
{"北京", {"AA", "XZZ"}}
};
// 第2区:黑龙江、辽宁、吉林
regionProvinceRules['2'] = {
{"黑龙江", {"AA", "HZZ"}},
{"吉林", {"IA", "PZZ"}},
{"辽宁", {"QA", "XZZ"}}
};
// 第3区:河北、内蒙古、山西、天津
regionProvinceRules['3'] = {
{"天津", {"AA", "FZZ"}},
{"内蒙古", {"GA", "LZZ"}},
{"河北", {"MA", "RZZ"}},
{"山西", {"SA", "XZZ"}}
};
// 第4区:山东、江苏、上海
regionProvinceRules['4'] = {
{"上海", {"AA", "HZZ"}},
{"山东", {"IA", "PZZ"}},
{"江苏", {"QA", "XZZ"}}
};
// 第5区:浙江、江西、福建
regionProvinceRules['5'] = {
{"浙江", {"AA", "HZZ"}},
{"江西", {"IA", "PZZ"}},
{"福建", {"QA", "XZZ"}}
};
// 第6区:河南、湖北、安徽
regionProvinceRules['6'] = {
{"安徽", {"AA", "HZZ"}},
{"河南", {"IA", "PZZ"}},
{"湖北", {"QA", "XZZ"}}
};
// 第7区:湖南、广东、广西、海南
regionProvinceRules['7'] = {
{"湖南", {"AA", "HZZ"}},
{"广东", {"IA", "PZZ"}},
{"广西", {"QA", "XZZ"}},
{"海南", {"YA", "ZZZ"}}
};
// 第8区:四川、贵州、云南、重庆
regionProvinceRules['8'] = {
{"四川", {"AA", "FZZ"}},
{"重庆", {"GA", "LZZ"}},
{"贵州", {"MA", "RZZ"}},
{"云南", {"SA", "XZZ"}}
};
// 第9区:陕西、甘肃、宁夏、青海
regionProvinceRules['9'] = {
{"陕西", {"AA", "FZZ"}},
{"甘肃", {"GA", "LZZ"}},
{"宁夏", {"MA", "RZZ"}},
{"青海", {"SA", "XZZ"}}
};
// 第0区:新疆、西藏
regionProvinceRules['0'] = {
{"新疆", {"AA", "FZZ"}},
{"西藏", {"GA", "LZZ"}}
};
return regionProvinceRules;
}
// 判断后缀是否在范围内
bool isWithinRange(const std::string &suffix, const std::string &rangeStart, const std::string &rangeEnd) {
return suffix >= rangeStart && suffix <= rangeEnd;
}
// 查询呼号
void queryCallSign(const std::string &callSign) {
// 加载各类规则
auto countryRules = loadCountryPrefixRules();
auto stationTypeRules = loadStationTypeRules();
auto regionProvinceRules = loadRegionProvinceRules();
// 确保呼号至少有5位
if (callSign.length() < 5) {
std::cout << "呼号格式不正确" << std::endl;
return;
}
// 解析国家前缀
std::string country = countryRules;
// 解析电台种类
char stationType = callSign;
std::string station = stationTypeRules;
// 解析地区分区
char region = callSign;
// 解析后缀
std::string suffix = callSign.substr(3, 2); // 假设后缀长度为2位
// 查找该地区的省份范围
std::string provinceName = "未知省份";
if (regionProvinceRules.find(region) != regionProvinceRules.end()) {
for (const auto &province: regionProvinceRules) {
if (isWithinRange(suffix, province.second.first, province.second.second)) {
provinceName = province.first;
break;
}
}
}
// 处理特殊情况(业余信标台、卫星业余电台、特设业余电台)
if (stationType == 'J') {
char suffix1 = callSign;
if (suffix1 == 'H' || suffix1 == 'V' || suffix1 == 'U') {
station = "HF、VHF、UHV频段信标台";
} else if (suffix1 == 'D') {
station = "业余无线电测向专用信标台";
} else if (suffix1 == 'S' && region == '1') {
station = "卫星业余电台";
} else if (suffix1 == 'T') {
station = "特设业余电台";
}
}
// 输出结果
if (!country.empty() && !station.empty() && provinceName != "未知省份") {
std::cout << "呼号: " << callSign << std::endl;
std::cout << "国家: " << country << std::endl;
std::cout << "电台种类: " << station << std::endl;
std::cout << "省份: " << provinceName << std::endl;
} else {
std::cout << "未找到呼号对应的信息" << std::endl;
}
}
int main() {
std::string callSign;
std::cout << "请输入要查询的呼号: ";
std::cin >> callSign;
std::cout << "\n查询结果如下:\n" << std::endl;
// 查询呼号信息
queryCallSign(callSign);
system("pause");
return 0;
}
```
## 尾声
**后续计划:**
- [ ] 支持全球呼号
- [ ] 添加GUI
有更多想法?欢迎讨论👏 谢谢分享,切实解决生活中实际问题的作品。 arthas19 发表于 2024-10-2 02:56
谢谢分享,代码靓丽;
现在如果是做GUI,是用QT么,还是用VS的?
感谢发表意见,目前还没着手研究GUI,所以暂时无法确定使用何种GUI框架。至于映射表写在代码中是因为中国呼号命名规则几乎不会变动,写死是一种比较简单的方式。后续在添加全球呼号支持时,一定是会把映射表单独写到文件中,通过加载映射文件来解析呼号的。 谢谢分享 这个厉害啊,明年考初级证,准备弄个小电台玩
单位领导说,中级证之后可以让我拿单位的练练 先插个眼待后面需要取 简单明了,学习c++有用。 kim945 发表于 2024-10-1 01:19
这个厉害啊,明年考初级证,准备弄个小电台玩
单位领导说,中级证之后可以让我拿单位的练练
今年先把初级先考了吧,中级需要初级证持有一段时间才能再考 没有证 来学习一下
谢谢分享,代码靓丽;
现在如果是做GUI,是用QT么,还是用VS的?
不如不忙着搞GUI,映射表如果不写在代码里面,写在外面,会不会更灵活一些 感谢分享,现在能做c/c++的好像越来越少了!
页:
[1]
2