In generic format examples, the latitude is shown as the 8-character string ddmm.hhN (i.e. degrees, minutes and hundredths of a minute north).
In generic format examples, the longitude is shown as the 9-character string dddmm.hhW (i.e. degrees, minutes and hundredths of a minute west).
Should probably split these into two regex statements.
https://www.aprs.org/doc/APRS101.PDF
> In generic format examples, the latitude is shown as the 8-character string
`ddmm.hhN` (i.e. degrees, minutes and hundredths of a minute north).
> In generic format examples, the longitude is shown as the 9-character string
`dddmm.hhW` (i.e. degrees, minutes and hundredths of a minute west).
Should probably split these into two regex statements.
<img width="984" alt="image.png" src="attachments/7c9eed88-2c85-469f-9fe5-c78f462b8f6c">
https://regex101.com/
Made a ton of progress tonight. This should be able to be turned into a function. Not committed to this repo yet. I learned a lot about capture groups!
Currently it does account for the position ambiguity in the APRS spec (you can put spaces in), but I ran out of time to
fix the values if you put in spaces for mm and
apply the same ambiguity to lon if ambiguity is not defined with spaces
properly error out if it can't parse any location
In some instances — for example, where the exact position is not known — the sending station may wish to reduce the number of digits of precision in the latitude and longitude. In this case, the mm and hh digits in the latitude may be progressively replaced by a V (space) character as the amount of imprecision increases. [...]
use regex::Regex;
fn main() {
//let packet = "1:Fm KC2IHX-7 To BEACON <UI pid=F0 Len=64 > !4603.08N/11819.83WB -1/B -7/N 14105/7140LSB DN06ub Net105&FARPN";
let packet = "1:Fm W1CDN-7 To BEACON <UI pid=F0 Len=50 > !4756.48N/09701.61WB -1/B -7/N EN17lw Net105&FARPN";
let re_loc = Regex::new(r"(?P<latd>\d\d)(?P<latm>[\d ][\d ]\.[\d ][\d ])(?P<ns>[NS])/(?P<lond>\d\d\d)(?P<lonm>[\d ][\d ]\.[\d ][\d ])(?P<ew>[EW])").unwrap();
let loc = re_loc.captures(&packet).unwrap();
//println!("latd {} {} lond {} {}", &loc["latd"], &loc["ns"], &loc["lond"], &loc["ew"]);
// Oh no, I have to convert these to decimal degrees...
/*
https://www.aprs.org/doc/APRS101.PDF
- In generic format examples, the latitude is shown as the 8-character string
ddmm.hhN (i.e. degrees, minutes and hundredths of a minute north).
- In generic format examples, the longitude is shown as the 9-character string
dddmm.hhW (i.e. degrees, minutes and hundredths of a minute west).
*/
// Convert to decimal degrees
let mut lat_dec: f64 = &loc["latd"].trim().parse().expect("Expects a number!") + (&loc["latm"].trim().parse().expect("Expects a number!") / 60.0);
// If south, make negative
if &loc["ns"] == "S" {
lat_dec = lat_dec * -1.0
}
//println!("lat {}", lat_dec);
let mut lon_dec: f64 = &loc["lond"].trim().parse().expect("Expects a number!") + (&loc["lonm"].trim().parse().expect("Expects a number!") / 60.0);
// If west, make negative
if &loc["ew"] == "W" {
lon_dec = lon_dec * -1.0
}
//println!("lon {}", lon_dec);
// String to paste into map for testing
println!("{}, {}", lat_dec, lon_dec);
}
Made a ton of progress tonight. This should be able to be turned into a function. Not committed to this repo yet. I learned a lot about capture groups!
Currently it *does* account for the position ambiguity in the APRS spec (you can put spaces in), but I ran out of time to
- [x] fix the values if you put in spaces for `mm` and
- [ ] apply the same ambiguity to `lon` if ambiguity is not defined with spaces
- [x] properly error out if it can't parse any location
> In some instances — for example, where the exact position is not known — the sending station may wish to reduce the number of digits of precision in the latitude and longitude. In this case, the mm and hh digits in the latitude may be progressively replaced by a V (space) character as the amount of imprecision increases. [...]
```
use regex::Regex;
fn main() {
//let packet = "1:Fm KC2IHX-7 To BEACON <UI pid=F0 Len=64 > !4603.08N/11819.83WB -1/B -7/N 14105/7140LSB DN06ub Net105&FARPN";
let packet = "1:Fm W1CDN-7 To BEACON <UI pid=F0 Len=50 > !4756.48N/09701.61WB -1/B -7/N EN17lw Net105&FARPN";
let re_loc = Regex::new(r"(?P<latd>\d\d)(?P<latm>[\d ][\d ]\.[\d ][\d ])(?P<ns>[NS])/(?P<lond>\d\d\d)(?P<lonm>[\d ][\d ]\.[\d ][\d ])(?P<ew>[EW])").unwrap();
let loc = re_loc.captures(&packet).unwrap();
//println!("latd {} {} lond {} {}", &loc["latd"], &loc["ns"], &loc["lond"], &loc["ew"]);
// Oh no, I have to convert these to decimal degrees...
/*
https://www.aprs.org/doc/APRS101.PDF
- In generic format examples, the latitude is shown as the 8-character string
ddmm.hhN (i.e. degrees, minutes and hundredths of a minute north).
- In generic format examples, the longitude is shown as the 9-character string
dddmm.hhW (i.e. degrees, minutes and hundredths of a minute west).
*/
// Convert to decimal degrees
let mut lat_dec: f64 = &loc["latd"].trim().parse().expect("Expects a number!") + (&loc["latm"].trim().parse().expect("Expects a number!") / 60.0);
// If south, make negative
if &loc["ns"] == "S" {
lat_dec = lat_dec * -1.0
}
//println!("lat {}", lat_dec);
let mut lon_dec: f64 = &loc["lond"].trim().parse().expect("Expects a number!") + (&loc["lonm"].trim().parse().expect("Expects a number!") / 60.0);
// If west, make negative
if &loc["ew"] == "W" {
lon_dec = lon_dec * -1.0
}
//println!("lon {}", lon_dec);
// String to paste into map for testing
println!("{}, {}", lat_dec, lon_dec);
}
```
<img width="989" alt="image.png" src="attachments/35e61d56-ee7b-47fb-bc79-93969ebfa083">
Another snapshot. This replaces ambiguation spaces with a random integer, but I didn't carry the same pattern over yet.
check that all capture groups have values
use regex::Regex;
use rand::Rng;
fn main() {
//let packet = "1:Fm KC2IHX-7 To BEACON <UI pid=F0 Len=64 > !4603.08N/11819.83WB -1/B -7/N 14105/7140LSB DN06ub Net105&FARPN";
let packet = "1:Fm W1CDN-7 To BEACON <UI pid=F0 Len=50 > !47 . n/09701.61WB -1/B -7/N EN17lw Net105&FARPN";
// Capture different pieces of the location stringf
let re_loc = Regex::new(r"(?P<latd>\d{2})(?P<latm>[\d ]{2}\.[\d ]{2})(?P<ns>[nsNS])/(?P<lond>\d{3})(?P<lonm>[\d ]{2}\.[\d ]{2})(?P<ew>[ewEW])").unwrap();
let loc = re_loc.captures(&packet).unwrap();
//println!("latd {} {} lond {} {}", &loc["latd"], &loc["ns"], &loc["lond"], &loc["ew"]);
// TODO check that each capture group has information, otherwise return nothing
// Oh no, I have to convert these to decimal degrees...
/*
https://www.aprs.org/doc/APRS101.PDF
- In generic format examples, the latitude is shown as the 8-character string
ddmm.hhN (i.e. degrees, minutes and hundredths of a minute north).
- In generic format examples, the longitude is shown as the 9-character string
dddmm.hhW (i.e. degrees, minutes and hundredths of a minute west).
*/
// Initiate randomness for ambiguity..
let mut rng = rand::rng();
// Convert to decimal degrees. If ambiguity spaces are included (see APRS spec), replace them with 1s.
let mut lat_dec: f64 = &loc["latd"].trim().parse().expect("Expects a number!") + (&loc["latm"].replace(" ", &rng.random_range(0..9).to_string()).trim().parse().expect("Expects a number!") / 60.0);
// If south, make negative
if &loc["ns"] == "S" {
lat_dec = lat_dec * -1.0
}
// TODO if there are spaces in loc["latm"], the same spaces need to be in loc["lonm"] for proper ambiguity according to APRS spec
let mut lon_dec: f64 = &loc["lond"].trim().parse().expect("Expects a number!") + (&loc["lonm"].replace(" ", &rng.random_range(0..9).to_string()).trim().parse().expect("Expects a number!") / 60.0);
// If west, make negative
if &loc["ew"] == "W" {
lon_dec = lon_dec * -1.0
}
// String to paste into map for testing
println!("{}, {}", lat_dec, lon_dec);
}
Another snapshot. This replaces ambiguation spaces with a random integer, but I didn't carry the same pattern over yet.
- [x] check that all capture groups have values
```
use regex::Regex;
use rand::Rng;
fn main() {
//let packet = "1:Fm KC2IHX-7 To BEACON <UI pid=F0 Len=64 > !4603.08N/11819.83WB -1/B -7/N 14105/7140LSB DN06ub Net105&FARPN";
let packet = "1:Fm W1CDN-7 To BEACON <UI pid=F0 Len=50 > !47 . n/09701.61WB -1/B -7/N EN17lw Net105&FARPN";
// Capture different pieces of the location stringf
let re_loc = Regex::new(r"(?P<latd>\d{2})(?P<latm>[\d ]{2}\.[\d ]{2})(?P<ns>[nsNS])/(?P<lond>\d{3})(?P<lonm>[\d ]{2}\.[\d ]{2})(?P<ew>[ewEW])").unwrap();
let loc = re_loc.captures(&packet).unwrap();
//println!("latd {} {} lond {} {}", &loc["latd"], &loc["ns"], &loc["lond"], &loc["ew"]);
// TODO check that each capture group has information, otherwise return nothing
// Oh no, I have to convert these to decimal degrees...
/*
https://www.aprs.org/doc/APRS101.PDF
- In generic format examples, the latitude is shown as the 8-character string
ddmm.hhN (i.e. degrees, minutes and hundredths of a minute north).
- In generic format examples, the longitude is shown as the 9-character string
dddmm.hhW (i.e. degrees, minutes and hundredths of a minute west).
*/
// Initiate randomness for ambiguity..
let mut rng = rand::rng();
// Convert to decimal degrees. If ambiguity spaces are included (see APRS spec), replace them with 1s.
let mut lat_dec: f64 = &loc["latd"].trim().parse().expect("Expects a number!") + (&loc["latm"].replace(" ", &rng.random_range(0..9).to_string()).trim().parse().expect("Expects a number!") / 60.0);
// If south, make negative
if &loc["ns"] == "S" {
lat_dec = lat_dec * -1.0
}
// TODO if there are spaces in loc["latm"], the same spaces need to be in loc["lonm"] for proper ambiguity according to APRS spec
let mut lon_dec: f64 = &loc["lond"].trim().parse().expect("Expects a number!") + (&loc["lonm"].replace(" ", &rng.random_range(0..9).to_string()).trim().parse().expect("Expects a number!") / 60.0);
// If west, make negative
if &loc["ew"] == "W" {
lon_dec = lon_dec * -1.0
}
// String to paste into map for testing
println!("{}, {}", lat_dec, lon_dec);
}
```
More progress. I got really slowed down looking for a way to return a None tuple that was also f64.
use regex::Regex;
use rand::Rng;
fn main() {
//let packet = "1:Fm KC2IHX-7 To BEACON <UI pid=F0 Len=64 > !4603.08N/11819.83WB -1/B -7/N 14105/7140LSB DN06ub Net105&FARPN"; // full location
let packet = "1:Fm KC2IHX-7 To BEACON <UI pid=F0 Len=64 > !4603.08/11819.83WB -1/B -7/N 14105/7140LSB DN06ub Net105&FARPN"; // missing capture group N
//let packet = "1:Fm W1CDN-7 To BEACON <UI pid=F0 Len=50 > !47 . n/09701.61WB -1/B -7/N EN17lw Net105&FARPN"; // ambiguity
//let packet = " 1:Fm K7SKI To BEACON <UI pid=F0 Len=58 > K7SKI-1/mail K7SKI-7/K-node and hf-vhf gateway Boise, ID"; // no location
let (a, b) = aprs_loc(packet);
println!("{}, {}", a, b);
// Mimic checks that need to be done on the function result
if a == -9999.0_f64 {
println!("lat is null, don't include it in the spot")
}
if b == -9999.0_f64 {
println!("lon is null, don't include it in the spot")
}
}
fn aprs_loc(packet: &str) -> (f64, f64) {
// Capture different pieces of the location stringf
let re_loc = Regex::new(r"(?P<latd>\d{2})(?P<latm>[\d ]{2}\.[\d ]{2})(?P<ns>[nsNS])/(?P<lond>\d{3})(?P<lonm>[\d ]{2}\.[\d ]{2})(?P<ew>[ewEW])").unwrap();
// Only proceed if there were captures
match re_loc.captures(&packet) {
Some(_caps) => {
let loc = re_loc.captures(&packet).unwrap();
// Initiate randomness for ambiguity..
let mut rng = rand::rng();
// Convert to decimal degrees. If ambiguity spaces are included (see APRS spec), replace them with 1s.
let mut lat_dec: f64 = &loc["latd"].trim().parse().expect("Expects a number!") + (&loc["latm"].replace(" ", &rng.random_range(0..9).to_string()).trim().parse().expect("Expects a number!") / 60.0);
// If south, make negative
if &loc["ns"] == "S" {
lat_dec = lat_dec * -1.0
}
// TODO if there are spaces in loc["latm"], the same spaces need to be in loc["lonm"] for proper ambiguity according to APRS spec
let mut lon_dec: f64 = &loc["lond"].trim().parse().expect("Expects a number!") + (&loc["lonm"].replace(" ", &rng.random_range(0..9).to_string()).trim().parse().expect("Expects a number!") / 60.0);
// If west, make negative
if &loc["ew"] == "W" {
lon_dec = lon_dec * -1.0
}
// String to paste into map for testing
//println!("{}, {}", lat_dec, lon_dec);
// Return
(lat_dec, lon_dec)
}
// Otherwise if there were no captures, return bad data
None => {
// The regex did not match. Deal with it here!
(-9999.0_f64, -9999.0_f64)
}
}
}
More progress. I got really slowed down looking for a way to return a None tuple that was also f64.
```
use regex::Regex;
use rand::Rng;
fn main() {
//let packet = "1:Fm KC2IHX-7 To BEACON <UI pid=F0 Len=64 > !4603.08N/11819.83WB -1/B -7/N 14105/7140LSB DN06ub Net105&FARPN"; // full location
let packet = "1:Fm KC2IHX-7 To BEACON <UI pid=F0 Len=64 > !4603.08/11819.83WB -1/B -7/N 14105/7140LSB DN06ub Net105&FARPN"; // missing capture group N
//let packet = "1:Fm W1CDN-7 To BEACON <UI pid=F0 Len=50 > !47 . n/09701.61WB -1/B -7/N EN17lw Net105&FARPN"; // ambiguity
//let packet = " 1:Fm K7SKI To BEACON <UI pid=F0 Len=58 > K7SKI-1/mail K7SKI-7/K-node and hf-vhf gateway Boise, ID"; // no location
let (a, b) = aprs_loc(packet);
println!("{}, {}", a, b);
// Mimic checks that need to be done on the function result
if a == -9999.0_f64 {
println!("lat is null, don't include it in the spot")
}
if b == -9999.0_f64 {
println!("lon is null, don't include it in the spot")
}
}
fn aprs_loc(packet: &str) -> (f64, f64) {
// Capture different pieces of the location stringf
let re_loc = Regex::new(r"(?P<latd>\d{2})(?P<latm>[\d ]{2}\.[\d ]{2})(?P<ns>[nsNS])/(?P<lond>\d{3})(?P<lonm>[\d ]{2}\.[\d ]{2})(?P<ew>[ewEW])").unwrap();
// Only proceed if there were captures
match re_loc.captures(&packet) {
Some(_caps) => {
let loc = re_loc.captures(&packet).unwrap();
// Initiate randomness for ambiguity..
let mut rng = rand::rng();
// Convert to decimal degrees. If ambiguity spaces are included (see APRS spec), replace them with 1s.
let mut lat_dec: f64 = &loc["latd"].trim().parse().expect("Expects a number!") + (&loc["latm"].replace(" ", &rng.random_range(0..9).to_string()).trim().parse().expect("Expects a number!") / 60.0);
// If south, make negative
if &loc["ns"] == "S" {
lat_dec = lat_dec * -1.0
}
// TODO if there are spaces in loc["latm"], the same spaces need to be in loc["lonm"] for proper ambiguity according to APRS spec
let mut lon_dec: f64 = &loc["lond"].trim().parse().expect("Expects a number!") + (&loc["lonm"].replace(" ", &rng.random_range(0..9).to_string()).trim().parse().expect("Expects a number!") / 60.0);
// If west, make negative
if &loc["ew"] == "W" {
lon_dec = lon_dec * -1.0
}
// String to paste into map for testing
//println!("{}, {}", lat_dec, lon_dec);
// Return
(lat_dec, lon_dec)
}
// Otherwise if there were no captures, return bad data
None => {
// The regex did not match. Deal with it here!
(-9999.0_f64, -9999.0_f64)
}
}
}
```
// Starting from first example at https://rusting.substack.com/p/key-value-store-project
use std:: collections:: HashMap;
fn main() {
let mut loc_store = HashMap:: new();
let lat = 47.94116_f64;
let lon = -97.02683_f64;
let mut loc = vec![lat.to_string(), lon.to_string()];
// Insert some keys and values
//loc_store.insert("key1", "valuel");
//loc_store.insert("key2", "value2");
loc_store.insert("key3", loc);
// Retrieve the values
/*
match loc_store.get("key1") {
Some(value) => println!("{}: {:?}", "key1", value),
None => println! ("Key not found"),
}
match loc_store.get("key2") {
Some(value) => println!("{}: {:?}", "key2", value),
None => println! ("Key not found"),
}
*/
match loc_store.get("key3") {
Some(value) => println!("{}: lat {:?} lon {:?}", "key3", value[0], value[1]),
None => println! ("Key not found"),
}
}
Quick update on storing and retrieving values:
```
// Starting from first example at https://rusting.substack.com/p/key-value-store-project
use std:: collections:: HashMap;
fn main() {
let mut loc_store = HashMap:: new();
let lat = 47.94116_f64;
let lon = -97.02683_f64;
let mut loc = vec![lat.to_string(), lon.to_string()];
// Insert some keys and values
//loc_store.insert("key1", "valuel");
//loc_store.insert("key2", "value2");
loc_store.insert("key3", loc);
// Retrieve the values
/*
match loc_store.get("key1") {
Some(value) => println!("{}: {:?}", "key1", value),
None => println! ("Key not found"),
}
match loc_store.get("key2") {
Some(value) => println!("{}: {:?}", "key2", value),
None => println! ("Key not found"),
}
*/
match loc_store.get("key3") {
Some(value) => println!("{}: lat {:?} lon {:?}", "key3", value[0], value[1]),
None => println! ("Key not found"),
}
}
```
Blocking a user prevents them from interacting with repositories, such as opening or commenting on pull requests or issues. Learn more about blocking a user.
Separating this from #5.
Otherwise, N6CTA's mobile BBS won't show up in the right place. :-)
https://rust-lang-nursery.github.io/rust-cookbook/text/regex.html
https://www.aprs.org/doc/APRS101.PDF
Should probably split these into two regex statements.
Made a ton of progress tonight. This should be able to be turned into a function. Not committed to this repo yet. I learned a lot about capture groups!
Currently it does account for the position ambiguity in the APRS spec (you can put spaces in), but I ran out of time to
mmandlonif ambiguity is not defined with spacesI guess I could just replace the spaces with either
before converting string to numeric.
Another snapshot. This replaces ambiguation spaces with a random integer, but I didn't carry the same pattern over yet.
More progress. I got really slowed down looking for a way to return a None tuple that was also f64.
Quick update on storing and retrieving values: