mirror of
				https://gitea.farpn.net/w1cdn/mwtchahrd.git
				synced 2025-11-03 13:36:36 -06:00 
			
		
		
		
	Parse APRS location from packets that have that information.
This commit is contained in:
		@@ -8,6 +8,8 @@ authors = ["Chris, N6CTA  <mail@n6cta.com>"]
 | 
				
			|||||||
anyhow = "1.0"
 | 
					anyhow = "1.0"
 | 
				
			||||||
chrono = "0.4"
 | 
					chrono = "0.4"
 | 
				
			||||||
clap = { version = "4", features = ["derive"] }
 | 
					clap = { version = "4", features = ["derive"] }
 | 
				
			||||||
 | 
					rand = "0.9.2"
 | 
				
			||||||
 | 
					regex = "1.12.2"
 | 
				
			||||||
reqwest = { version = "0.12.24", features = ["json", "blocking"] }
 | 
					reqwest = { version = "0.12.24", features = ["json", "blocking"] }
 | 
				
			||||||
serde_json = "1.0.145"
 | 
					serde_json = "1.0.145"
 | 
				
			||||||
socket2 = "0.5"
 | 
					socket2 = "0.5"
 | 
				
			||||||
 
 | 
				
			|||||||
							
								
								
									
										58
									
								
								src/main.rs
									
									
									
									
									
								
							
							
						
						
									
										58
									
								
								src/main.rs
									
									
									
									
									
								
							@@ -12,6 +12,9 @@ use std::net::UdpSocket;
 | 
				
			|||||||
use std::net::Ipv4Addr;
 | 
					use std::net::Ipv4Addr;
 | 
				
			||||||
use serde_json::json;
 | 
					use serde_json::json;
 | 
				
			||||||
use reqwest;
 | 
					use reqwest;
 | 
				
			||||||
 | 
					use regex::Regex;
 | 
				
			||||||
 | 
					use rand::Rng;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
/// Validate that the provided port string can be parsed into a u16 and is nonzero.
 | 
					/// Validate that the provided port string can be parsed into a u16 and is nonzero.
 | 
				
			||||||
fn validate_port(port: &str) -> Result<u16, String> {
 | 
					fn validate_port(port: &str) -> Result<u16, String> {
 | 
				
			||||||
@@ -396,6 +399,9 @@ fn handle_frame(frame: &AgwFrame, cli: &Cli, buffers: &mut BufferManager) {
 | 
				
			|||||||
    } else {
 | 
					    } else {
 | 
				
			||||||
        basic_destination.clone()
 | 
					        basic_destination.clone()
 | 
				
			||||||
    };
 | 
					    };
 | 
				
			||||||
 | 
					    // Extract location from APRS format
 | 
				
			||||||
 | 
					    let (lat, lon) = aprs_loc(&text);
 | 
				
			||||||
 | 
					    //println!("{}, {}", lat, lon);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    // Ignore frames where the basic destination contains "NODES" (case‑insensitive).
 | 
					    // Ignore frames where the basic destination contains "NODES" (case‑insensitive).
 | 
				
			||||||
    if basic_destination.to_uppercase().contains("NODES") {
 | 
					    if basic_destination.to_uppercase().contains("NODES") {
 | 
				
			||||||
@@ -437,6 +443,8 @@ fn handle_frame(frame: &AgwFrame, cli: &Cli, buffers: &mut BufferManager) {
 | 
				
			|||||||
            "de_call": &spotter,
 | 
					            "de_call": &spotter,
 | 
				
			||||||
            "freq": &freq,
 | 
					            "freq": &freq,
 | 
				
			||||||
            "comment": &text,
 | 
					            "comment": &text,
 | 
				
			||||||
 | 
					            "dx_latitude": &lat,
 | 
				
			||||||
 | 
					            "dx_longitude": &lon,
 | 
				
			||||||
            "mode": "PKT",
 | 
					            "mode": "PKT",
 | 
				
			||||||
            "mode_type": "DATA",
 | 
					            "mode_type": "DATA",
 | 
				
			||||||
            "mode_source": "SPOT",
 | 
					            "mode_source": "SPOT",
 | 
				
			||||||
@@ -446,6 +454,7 @@ fn handle_frame(frame: &AgwFrame, cli: &Cli, buffers: &mut BufferManager) {
 | 
				
			|||||||
        let res = client.post(spothole_url)
 | 
					        let res = client.post(spothole_url)
 | 
				
			||||||
            .json(&packet)
 | 
					            .json(&packet)
 | 
				
			||||||
            .send();
 | 
					            .send();
 | 
				
			||||||
 | 
					        println!("sent = {}", packet);
 | 
				
			||||||
        println!("res = {res:?}");
 | 
					        println!("res = {res:?}");
 | 
				
			||||||
        
 | 
					        
 | 
				
			||||||
        
 | 
					        
 | 
				
			||||||
@@ -461,7 +470,9 @@ fn handle_frame(frame: &AgwFrame, cli: &Cli, buffers: &mut BufferManager) {
 | 
				
			|||||||
            "source": &source,
 | 
					            "source": &source,
 | 
				
			||||||
            "spotter": &spotter,
 | 
					            "spotter": &spotter,
 | 
				
			||||||
            "summary": &summary,
 | 
					            "summary": &summary,
 | 
				
			||||||
            "text": &text,
 | 
					            "text": &text,            
 | 
				
			||||||
 | 
					            "dx_latitude": &lat,
 | 
				
			||||||
 | 
					            "dx_longitude": &lon,
 | 
				
			||||||
            "freq": &freq,
 | 
					            "freq": &freq,
 | 
				
			||||||
            "timestamp": SystemTime::now().duration_since(UNIX_EPOCH).unwrap().as_secs(),
 | 
					            "timestamp": SystemTime::now().duration_since(UNIX_EPOCH).unwrap().as_secs(),
 | 
				
			||||||
            "type": "data"
 | 
					            "type": "data"
 | 
				
			||||||
@@ -569,4 +580,49 @@ fn main() -> Result<()> {
 | 
				
			|||||||
        println!("Disconnected. Reconnecting in {} ms...", reconnect_delay_ms);
 | 
					        println!("Disconnected. Reconnecting in {} ms...", reconnect_delay_ms);
 | 
				
			||||||
        sleep(Duration::from_millis(reconnect_delay_ms));
 | 
					        sleep(Duration::from_millis(reconnect_delay_ms));
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					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) => {
 | 
				
			||||||
 | 
					        
 | 
				
			||||||
 | 
					        // Break captures into named values
 | 
				
			||||||
 | 
					        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 random digits.
 | 
				
			||||||
 | 
					        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)
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
		Reference in New Issue
	
	Block a user