parent
b0621b636d
commit
e80521af14
|
@ -0,0 +1,2 @@
|
|||
[target.arm-unknown-linux-gnueabihf]
|
||||
linker = "arm-linux-gnueabihf-gcc"
|
|
@ -0,0 +1,3 @@
|
|||
[submodule "rpi-rgb-led-matrix"]
|
||||
path = rpi-rgb-led-matrix
|
||||
url = git@github.com:hzeller/rpi-rgb-led-matrix.git
|
|
@ -115,6 +115,7 @@ version = "0.1.0"
|
|||
dependencies = [
|
||||
"chrono",
|
||||
"config",
|
||||
"openssl",
|
||||
"reqwest",
|
||||
"rpi-led-matrix",
|
||||
"serde 1.0.126",
|
||||
|
@ -565,6 +566,15 @@ version = "0.1.4"
|
|||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "28988d872ab76095a6e6ac88d99b54fd267702734fd7ffe610ca27f533ddb95a"
|
||||
|
||||
[[package]]
|
||||
name = "openssl-src"
|
||||
version = "111.15.0+1.1.1k"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "b1a5f6ae2ac04393b217ea9f700cd04fa9bf3d93fae2872069f3d15d908af70a"
|
||||
dependencies = [
|
||||
"cc",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "openssl-sys"
|
||||
version = "0.9.63"
|
||||
|
@ -574,6 +584,7 @@ dependencies = [
|
|||
"autocfg",
|
||||
"cc",
|
||||
"libc",
|
||||
"openssl-src",
|
||||
"pkg-config",
|
||||
"vcpkg",
|
||||
]
|
||||
|
|
|
@ -3,10 +3,12 @@ name = "dsn-visualizer"
|
|||
version = "0.1.0"
|
||||
authors = ["Joey Hines <joey@ahines.net>"]
|
||||
edition = "2018"
|
||||
build = "build.rs"
|
||||
|
||||
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
|
||||
|
||||
[dependencies]
|
||||
openssl = { version = "0.10", features = ["vendored"] }
|
||||
reqwest = {version = "0.11", features=["blocking"]}
|
||||
chrono = {version = "0.4.19", features=["serde"]}
|
||||
xml-rs = "0.8.3"
|
||||
|
|
|
@ -0,0 +1,4 @@
|
|||
fn main() {
|
||||
println!("cargo:rustc-link-search=rpi-rgb-led-matrix/lib/");
|
||||
println!("cargo:rustc-flags=-l dylib=stdc++");
|
||||
}
|
|
@ -1,3 +1,7 @@
|
|||
dsn_address = "https://eyes.nasa.gov/dsn/data/dsn.xml"
|
||||
polling_rate = 5
|
||||
spacecraft = []
|
||||
|
||||
[[ spacecraft ]]
|
||||
name = "JNO"
|
||||
id = 61
|
||||
color = [235, 161, 101]
|
|
@ -0,0 +1 @@
|
|||
Subproject commit dfc27c15c224a92496034a39512a274744879e86
|
|
@ -1,18 +1,29 @@
|
|||
use serde::Deserialize;
|
||||
use config::{Config, ConfigError, File};
|
||||
use rpi_led_matrix::LedColor;
|
||||
use serde::Deserialize;
|
||||
|
||||
#[derive(Debug, Deserialize, Clone)]
|
||||
pub struct SpacecraftConfig {
|
||||
pub spacecraft_name: String,
|
||||
pub spacecraft_id: u64,
|
||||
pub color: (u8, u8, u8)
|
||||
pub color: (u8, u8, u8),
|
||||
}
|
||||
|
||||
impl SpacecraftConfig {
|
||||
fn color(&self) -> LedColor {
|
||||
LedColor {
|
||||
red: self.color.0,
|
||||
green: self.color.1,
|
||||
blue: self.color.2,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, Deserialize, Clone)]
|
||||
pub struct VisualizerConfig {
|
||||
pub dsn_address: String,
|
||||
pub polling_rate: u64,
|
||||
pub spacecraft: Vec<SpacecraftConfig>
|
||||
pub spacecraft: Vec<SpacecraftConfig>,
|
||||
}
|
||||
|
||||
impl VisualizerConfig {
|
||||
|
|
|
@ -49,7 +49,7 @@ impl std::fmt::Display for ParseError {
|
|||
#[derive(Debug)]
|
||||
pub enum DSNError {
|
||||
ParseErr(ParseError),
|
||||
ReqwestErr(reqwest::Error)
|
||||
ReqwestErr(reqwest::Error),
|
||||
}
|
||||
|
||||
impl From<ParseError> for DSNError {
|
||||
|
@ -68,8 +68,7 @@ impl std::fmt::Display for DSNError {
|
|||
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||
match self {
|
||||
DSNError::ParseErr(e) => write!(f, "Req Parse Error: {}", e),
|
||||
DSNError::ReqwestErr(e) => write!(f, "Reqwest Error: {}", e)
|
||||
DSNError::ReqwestErr(e) => write!(f, "Reqwest Error: {}", e),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -4,16 +4,16 @@ use std::io::BufReader;
|
|||
use std::time::{SystemTime, UNIX_EPOCH};
|
||||
|
||||
use reqwest::blocking::Client;
|
||||
use xml::EventReader;
|
||||
use xml::reader::XmlEvent;
|
||||
use xml::EventReader;
|
||||
|
||||
use error::ParseError;
|
||||
|
||||
use crate::dsn::models::{Dish, Signal, Station, Target};
|
||||
use crate::dsn::error::DSNError;
|
||||
use crate::dsn::models::{Dish, Signal, Station, Target};
|
||||
|
||||
pub mod models;
|
||||
pub mod error;
|
||||
pub mod models;
|
||||
|
||||
pub fn dsn_request(client: &Client, dsn_api_url: &str) -> Result<Vec<Station>, DSNError> {
|
||||
let mut params = HashMap::new();
|
||||
|
@ -25,11 +25,7 @@ pub fn dsn_request(client: &Client, dsn_api_url: &str) -> Result<Vec<Station>, D
|
|||
|
||||
params.insert("r", timestamp);
|
||||
|
||||
let resp = client
|
||||
.get(dsn_api_url)
|
||||
.query(¶ms)
|
||||
.send()?
|
||||
.text()?;
|
||||
let resp = client.get(dsn_api_url).query(¶ms).send()?.text()?;
|
||||
|
||||
let file = BufReader::new(resp.as_bytes());
|
||||
|
||||
|
|
|
@ -58,7 +58,7 @@ pub enum SignalType {
|
|||
Data,
|
||||
Carrier,
|
||||
None,
|
||||
Unkown(String)
|
||||
Unkown(String),
|
||||
}
|
||||
|
||||
impl From<String> for SignalType {
|
||||
|
@ -67,7 +67,7 @@ impl From<String> for SignalType {
|
|||
"data" => SignalType::Data,
|
||||
"carrier" => SignalType::Carrier,
|
||||
"none" => SignalType::None,
|
||||
_ => SignalType::Unkown(s)
|
||||
_ => SignalType::Unkown(s),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
53
src/main.rs
53
src/main.rs
|
@ -1,12 +1,15 @@
|
|||
use crate::dsn::dsn_request;
|
||||
use reqwest::blocking::Client;
|
||||
use crate::config::VisualizerConfig;
|
||||
use crate::dsn::dsn_request;
|
||||
use crate::dsn::models::{Signal, SignalType, Station, Target};
|
||||
use matrix::Display;
|
||||
use reqwest::blocking::Client;
|
||||
use rpi_led_matrix::{LedColor, LedMatrix, LedMatrixOptions, LedRuntimeOptions};
|
||||
use std::sync::mpsc;
|
||||
use std::time::Duration;
|
||||
use crate::dsn::models::{Station, SignalType};
|
||||
|
||||
mod dsn;
|
||||
mod config;
|
||||
mod dsn;
|
||||
mod matrix;
|
||||
|
||||
fn dsn_thread(config: VisualizerConfig, tx: mpsc::Sender<Vec<Station>>) {
|
||||
let client = Client::new();
|
||||
|
@ -28,30 +31,54 @@ fn dsn_thread(config: VisualizerConfig, tx: mpsc::Sender<Vec<Station>>) {
|
|||
}
|
||||
}
|
||||
|
||||
|
||||
fn main() {
|
||||
let (tx, rx) = mpsc::channel::<Vec<Station>>();
|
||||
let config = VisualizerConfig::new("config.toml").unwrap();
|
||||
let sleep_duration = Duration::from_millis(1);
|
||||
let mut x = 0;
|
||||
|
||||
let mut display = Display::new(64, 64);
|
||||
|
||||
std::thread::spawn(move || dsn_thread(config, tx));
|
||||
|
||||
loop {
|
||||
let stations = rx.recv().unwrap();
|
||||
let mut blu = 0;
|
||||
let mut red = 255;
|
||||
let stations = rx.recv_timeout(sleep_duration);
|
||||
if let Ok(stations) = stations {
|
||||
for station in stations {
|
||||
for dish in station.dishes {
|
||||
for target in dish.target {
|
||||
let up_signal = dish.up_signal.iter().find(|s| s.spacecraft_id == Some(target.id) && SignalType::Data == s.signal_type);
|
||||
let down_signal = dish.down_signal.iter().find(|s| s.spacecraft_id == Some(target.id) && SignalType::Data == s.signal_type);
|
||||
let up_signal = dish.up_signal.iter().find(|s| {
|
||||
s.spacecraft_id == Some(target.id) && SignalType::Data == s.signal_type
|
||||
});
|
||||
let down_signal = dish.down_signal.iter().find(|s| {
|
||||
s.spacecraft_id == Some(target.id) && SignalType::Data == s.signal_type
|
||||
});
|
||||
|
||||
if let Some(up_signal) = up_signal {
|
||||
println!("{}: Uplink Rate: {}, Up leg Distance: {}", target.name, up_signal.data_rate.unwrap_or(0.0), target.upleg_range)
|
||||
}
|
||||
else if let Some(down_signal) = down_signal {
|
||||
println!("{}: Downlink Rate: {}, Down leg Distance: {}", target.name, down_signal.data_rate.unwrap_or(0.0), target.downleg_range)
|
||||
if up_signal.is_some() || down_signal.is_some() {
|
||||
|
||||
let color = config.spacecraft.iter().find(|spacecraft| spacecraft.spacecraft_id == target.id)
|
||||
|
||||
display.update_status(
|
||||
LedColor {
|
||||
red: red,
|
||||
green: 0,
|
||||
blue: blu,
|
||||
},
|
||||
target,
|
||||
up_signal.cloned(),
|
||||
down_signal.cloned(),
|
||||
);
|
||||
|
||||
blu += 20;
|
||||
red -= 20;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
display.refresh_display();
|
||||
}
|
||||
}
|
||||
|
|
|
@ -0,0 +1,107 @@
|
|||
use crate::dsn::models::{Signal, Target};
|
||||
use rpi_led_matrix::{LedCanvas, LedColor, LedMatrix, LedMatrixOptions, LedRuntimeOptions};
|
||||
use std::collections::HashMap;
|
||||
|
||||
struct ColumnStatus {
|
||||
color: LedColor,
|
||||
target: Target,
|
||||
state: i32,
|
||||
up_signal: Option<Signal>,
|
||||
down_signal: Option<Signal>,
|
||||
}
|
||||
|
||||
pub struct Display {
|
||||
matrix: LedMatrix,
|
||||
column_statuses: HashMap<u32, ColumnStatus>,
|
||||
}
|
||||
|
||||
impl Display {
|
||||
pub fn new(width: u32, height: u32) -> Self {
|
||||
let mut matrix_config = LedMatrixOptions::new();
|
||||
|
||||
matrix_config.set_cols(width);
|
||||
matrix_config.set_rows(height);
|
||||
matrix_config.set_hardware_mapping("adafruit-hat-pwm");
|
||||
matrix_config.set_pwm_bits(7);
|
||||
|
||||
let mut rt_config = LedRuntimeOptions::new();
|
||||
rt_config.set_gpio_slowdown(0);
|
||||
|
||||
let matrix = LedMatrix::new(Some(matrix_config), Some(rt_config)).unwrap();
|
||||
|
||||
Self {
|
||||
matrix,
|
||||
column_statuses: HashMap::new(),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn update_status(
|
||||
&mut self,
|
||||
color: LedColor,
|
||||
target: Target,
|
||||
up_signal: Option<Signal>,
|
||||
down_signal: Option<Signal>,
|
||||
) {
|
||||
self.column_statuses.insert(
|
||||
target.id,
|
||||
ColumnStatus {
|
||||
color,
|
||||
target,
|
||||
state: 0,
|
||||
up_signal,
|
||||
down_signal,
|
||||
},
|
||||
);
|
||||
}
|
||||
|
||||
fn uplink_animation(canvas: &mut LedCanvas, col_status: &mut ColumnStatus, position: i32) {
|
||||
canvas.draw_line(
|
||||
col_status.state,
|
||||
(position) * 2,
|
||||
col_status.state - 5,
|
||||
(position) * 2,
|
||||
&col_status.color,
|
||||
);
|
||||
canvas.draw_line(
|
||||
col_status.state,
|
||||
(position) * 2 + 1,
|
||||
col_status.state - 5,
|
||||
(position) * 2 + 1,
|
||||
&col_status.color,
|
||||
);
|
||||
col_status.state = (col_status.state + 1) % canvas.canvas_size().0;
|
||||
}
|
||||
|
||||
fn downlink_animation(canvas: &mut LedCanvas, col_status: &mut ColumnStatus, position: i32) {
|
||||
canvas.draw_line(
|
||||
(canvas.canvas_size().0 - col_status.state) as i32,
|
||||
(position) * 2,
|
||||
(canvas.canvas_size().0 - col_status.state + 5) as i32,
|
||||
(position) * 2,
|
||||
&col_status.color,
|
||||
);
|
||||
canvas.draw_line(
|
||||
(canvas.canvas_size().0 - col_status.state) as i32,
|
||||
(position) * 2 + 1,
|
||||
(canvas.canvas_size().0 - col_status.state + 5) as i32,
|
||||
(position) * 2 + 1,
|
||||
&col_status.color,
|
||||
);
|
||||
col_status.state = (col_status.state + 1) % canvas.canvas_size().0;
|
||||
}
|
||||
|
||||
pub fn refresh_display(&mut self) {
|
||||
let mut canvas = self.matrix.offscreen_canvas();
|
||||
canvas.clear();
|
||||
let mut pos = 0;
|
||||
for (_, col_status) in self.column_statuses.iter_mut() {
|
||||
if let Some(up_signal) = &col_status.up_signal {
|
||||
Display::uplink_animation(&mut canvas, col_status, pos)
|
||||
} else if let Some(down_signal) = &col_status.down_signal {
|
||||
Display::downlink_animation(&mut canvas, col_status, pos)
|
||||
}
|
||||
pos += 1
|
||||
}
|
||||
self.matrix.swap(canvas);
|
||||
}
|
||||
}
|
Loading…
Reference in New Issue