some workings
This commit is contained in:
parent
ced2b9be8b
commit
55936f0af4
|
@ -0,0 +1,8 @@
|
|||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<project version="4">
|
||||
<component name="ProjectModuleManager">
|
||||
<modules>
|
||||
<module fileurl="file://$PROJECT_DIR$/.idea/rtwx.iml" filepath="$PROJECT_DIR$/.idea/rtwx.iml" />
|
||||
</modules>
|
||||
</component>
|
||||
</project>
|
|
@ -0,0 +1,14 @@
|
|||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<module type="EMPTY_MODULE" version="4">
|
||||
<component name="NewModuleRootManager">
|
||||
<content url="file://$MODULE_DIR$">
|
||||
<sourceFolder url="file://$MODULE_DIR$/nexrad-browser/src" isTestSource="false" />
|
||||
<sourceFolder url="file://$MODULE_DIR$/nexrad2/src" isTestSource="false" />
|
||||
<sourceFolder url="file://$MODULE_DIR$/nxar2/src" isTestSource="false" />
|
||||
<sourceFolder url="file://$MODULE_DIR$/rtwx/src" isTestSource="false" />
|
||||
<excludeFolder url="file://$MODULE_DIR$/target" />
|
||||
</content>
|
||||
<orderEntry type="inheritedJdk" />
|
||||
<orderEntry type="sourceFolder" forTests="false" />
|
||||
</component>
|
||||
</module>
|
|
@ -0,0 +1,38 @@
|
|||
# `CLF ...`
|
||||
Currently loaded file.
|
||||
|
||||
| Command | Description | Example |
|
||||
|-----------|-------------------------------------------------------------------------------------|-----------|
|
||||
| `CLF OV` | Overwrite the currently loaded file with a new file from the file selection dialog. | `CLF OV` |
|
||||
| `CLF LD` | Load the currently loaded file into the scope. | `CLF LD` |
|
||||
| `CLF CLR` | Clear the currently loaded file. | `CLF CLR` |
|
||||
|
||||
# `POS ...`
|
||||
Position.
|
||||
|
||||
| Command | Description | Example |
|
||||
|----------------------------------|-------------------------------------------------------------|----------------------------|
|
||||
| `POS SHOW` | Show the current position in latitude/longitude. | `POS SHOW` |
|
||||
| `POS SLL [latitude] [longitude]` | Set the position to the desired latitude/longitude. | `POS SLL 38.8977 -77.0366` |
|
||||
| `POS SITE [site]` | Set the position to the position of the desired radar site. | `POS SITE KRAX` |
|
||||
|
||||
# `PREF` and `PREFSET`
|
||||
Preferences.
|
||||
|
||||
| Command | Description | Example |
|
||||
|---------------------------|---------------------------------------------------|--------------------------------|
|
||||
| `PREF SHOW [pref]` | Show the value of the preference. | `PREF SHOW RR` |
|
||||
| `PREF SET [pref] [value]` | Set the value of the preference. | `PREF SET RR 5` |
|
||||
| `PREF CLR [pref]` | Clear the prefernece (return to default value) | `PREF CLEAR RR` |
|
||||
| `PREFSET SAVE [name]` | Save the current setup of the scope as a prefset. | `PREF SET SAVE krax_tornadoes` |
|
||||
| `PREFSET RCL [name]` | Load the scope from the given prefset. | `PREF SET RCL krax_tornadoes` |
|
||||
| `PREFSET CLR [name]` | Remove the saved prefset. | `PREF SET CLR krax_tornadoes` |
|
||||
| `PREFSET CLRA` | Remove all saved prefsets. | `PREF SET CLRA` |
|
||||
|
||||
## Available Preferences
|
||||
|
||||
| Preference | Description | Value |
|
||||
|------------|---------------------|----------------------------|
|
||||
| RR | Range Rings | 1-100 (default 5) |
|
||||
| RREN | Range Rings Enabled | true, false (default true) |
|
||||
| FCS | Font Character Size | 1-100 (default 12) |
|
|
@ -2,36 +2,27 @@
|
|||
<head>
|
||||
<title>NEXRAD Browser</title>
|
||||
<style>
|
||||
.grid-parent {
|
||||
display: grid;
|
||||
grid-template-columns: 2fr .5fr;
|
||||
grid-template-rows: 1fr;
|
||||
grid-column-gap: 5px;
|
||||
grid-row-gap: 0px;
|
||||
@import url('https://fonts.googleapis.com/css2?family=VT323&display=swap');
|
||||
|
||||
#canvas {
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
image-rendering: pixelated;
|
||||
}
|
||||
|
||||
.canvas-container {
|
||||
border-right: 2px solid black;
|
||||
height: 100%;
|
||||
body {
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
}
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
<div class="grid-parent">
|
||||
<div class="canvas-container">
|
||||
<canvas id="canvas"></canvas>
|
||||
</div>
|
||||
<div>
|
||||
<label for="file">Upload Archive II file</label>
|
||||
<input type="file" id="file" />
|
||||
<button id="go">Load</button>
|
||||
<hr>
|
||||
</div>
|
||||
</div>
|
||||
<canvas id="canvas">
|
||||
Something went wrong while loading the canvas. Sorry :/
|
||||
</canvas>
|
||||
|
||||
<p style="font-family: 'vt323', monospace;">FONT TEST!</p>
|
||||
|
||||
<script src="bootstrap.js"></script>
|
||||
|
||||
</body>
|
||||
</html>
|
|
@ -4,14 +4,69 @@ wasm.__nxrd_browser_init();
|
|||
|
||||
console.log("[JS] setup event listeners");
|
||||
|
||||
const fileSelector = document.getElementById('file');
|
||||
console.log("[JS] initializing the renderer");
|
||||
|
||||
document.getElementById("go").addEventListener("click", (e) => {
|
||||
const file = fileSelector.files[0];
|
||||
const reader = new FileReader();
|
||||
reader.addEventListener('load', (event) => {
|
||||
let data = event.target.result;
|
||||
wasm.load_ar2(data);
|
||||
});
|
||||
reader.readAsArrayBuffer(file);
|
||||
});
|
||||
|
||||
|
||||
const DEFAULT_PREFERENCES = {
|
||||
RR: 5,
|
||||
RREN: true,
|
||||
FCS: 16
|
||||
};
|
||||
let preferences = DEFAULT_PREFERENCES;
|
||||
|
||||
function get_font_size() { return `${preferences.FCS}px `; }
|
||||
|
||||
const canvas = document.getElementById("canvas");
|
||||
const ctx = canvas.getContext("2d");
|
||||
|
||||
const FIFTY_MILES = 0.0916;
|
||||
let current_lat = 38.8977;
|
||||
let current_long = -77.036560;
|
||||
|
||||
function calcRenderbox() {
|
||||
return [current_long - FIFTY_MILES, current_lat - FIFTY_MILES, current_long + FIFTY_MILES, current_lat + FIFTY_MILES];
|
||||
}
|
||||
|
||||
function latlongXY(lat, long) {
|
||||
let bbox = calcRenderbox();
|
||||
let pixelWidth = canvas.width;
|
||||
let pixelHeight = canvas.height;
|
||||
let bboxWidth = bbox[2] - bbox[0];
|
||||
let bboxHeight = bbox[3] - bbox[1];
|
||||
let widthPct = ( long - bbox[0] ) / bboxWidth;
|
||||
let heightPct = ( lat - bbox[1] ) / bboxHeight;
|
||||
let x = Math.floor( pixelWidth * widthPct );
|
||||
let y = Math.floor( pixelHeight * ( 1 - heightPct ) );
|
||||
return { x, y };
|
||||
}
|
||||
|
||||
let x0 = 0;
|
||||
let y0 = 0;
|
||||
let xfull = canvas.width;
|
||||
let yfull = canvas.height;
|
||||
|
||||
function recalcBorderCoordinates() {
|
||||
let xy = latlongXY(current_lat, current_long);
|
||||
|
||||
x0 = xy.x - canvas.width;
|
||||
y0 = xy.y - canvas.height;
|
||||
xfull = xy.x + (canvas.width / 2);
|
||||
yfull = xy.y + (canvas.height / 2);
|
||||
}
|
||||
|
||||
setInterval(() => {
|
||||
ctx.clearRect(0, 0, canvas.width, canvas.height);
|
||||
ctx.font = get_font_size();
|
||||
ctx.resetTransform();
|
||||
|
||||
recalcBorderCoordinates();
|
||||
|
||||
let xy = latlongXY(current_lat, current_long);
|
||||
|
||||
ctx.translate(xy.x, xy.y);
|
||||
|
||||
// background (black, always)
|
||||
ctx.fillStyle = "black";
|
||||
ctx.fillRect(x0, y0, xfull, yfull);
|
||||
})
|
|
@ -6,6 +6,13 @@ edition = "2021"
|
|||
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
|
||||
|
||||
[dependencies]
|
||||
#bzip2-rs = "0.1"
|
||||
bzip2 = "0.4"
|
||||
|
||||
bzip2-rs = { version = "0.1", optional = true }
|
||||
bzip2 = { version = "0.4", optional = true }
|
||||
|
||||
log = "0.4"
|
||||
|
||||
[features]
|
||||
default = ["bzip-impl-bzip2-rs"]
|
||||
bzip-impl-libbzip2 = ["bzip2"]
|
||||
bzip-impl-bzip2-rs = ["bzip2-rs"]
|
|
@ -0,0 +1,26 @@
|
|||
#[cfg(feature = "bzip-impl-libbzip2")]
|
||||
pub mod _internal_ffi {
|
||||
use bzip2::read::BzDecoder;
|
||||
|
||||
pub type BzipImpl<T> = BzDecoder<T>;
|
||||
}
|
||||
#[cfg(feature = "bzip-impl-bzip2-rs")]
|
||||
pub mod _internal_pure {
|
||||
use bzip2_rs::DecoderReader;
|
||||
|
||||
pub type BzipImpl<T> = DecoderReader<T>;
|
||||
}
|
||||
|
||||
#[cfg(all(feature = "bzip-impl-bzip2-rs", feature = "bzip-impl-libbzip2"))]
|
||||
compile_error!("Only one bzip2 implementation can be used. Please select bzip-impl-bzip2-rs or bzip-impl-libbzip2, but not both.");
|
||||
|
||||
#[cfg(not(any(feature = "bzip-impl-bzip2-rs", feature = "bzip-impl-libbzip2")))]
|
||||
compile_error!("A bzip2 implementation is required. Please select either bzip-impl-bzip2-rs or bzip-impl-libbzip2.");
|
||||
|
||||
#[cfg(all(target_arch = "wasm32", feature = "bzip-impl-libbzip2"))]
|
||||
compile_error!("bzip-impl-libbzip2 is not compatible with WebAssembly targets. Please use bzip-impl-bzip2-rs instead.");
|
||||
|
||||
#[cfg(feature = "bzip-impl-libbzip2")]
|
||||
pub type BzipDecoder<T> = _internal_ffi::BzipImpl<T>;
|
||||
#[cfg(feature = "bzip-impl-bzip2-rs")]
|
||||
pub type BzipDecoder<T> = _internal_pure::BzipImpl<T>;
|
|
@ -6,11 +6,12 @@ use std::fmt::{Display, Formatter};
|
|||
use std::io;
|
||||
use std::io::{Cursor, Read, Seek, SeekFrom};
|
||||
use std::str::Utf8Error;
|
||||
use bzip2::read::BzDecoder;
|
||||
//use bzip2_rs::DecoderReader;
|
||||
use log::{debug, trace};
|
||||
use crate::bzip::BzipDecoder;
|
||||
use crate::message::{LEGACY_CTM_HEADER_LEN, MESSAGE_BODY_SIZE, MESSAGE_HEADER_SIZE, MessageHeader};
|
||||
|
||||
pub mod message;
|
||||
pub mod bzip;
|
||||
|
||||
#[derive(Debug)]
|
||||
pub struct Nexrad2Chunk {
|
||||
|
@ -77,26 +78,6 @@ impl Error for NexradParseError {}
|
|||
|
||||
pub const NEXRAD2_META_CHUNK_FIXED_LENGTH: usize = 325888;
|
||||
|
||||
pub const MESSAGE_HEADER_SIZE: usize = 2 + 1 + 1 + 2 + 2 + 4 + 2 + 2;
|
||||
|
||||
#[derive(Debug)]
|
||||
pub struct MessageHeader {
|
||||
pub message_size: u16,
|
||||
pub rda_redundant_channel: u8,
|
||||
pub message_type: u8,
|
||||
pub message_sequence_number: u16,
|
||||
pub julian_date: u16,
|
||||
pub millis_after_midnight: u32,
|
||||
pub num_of_message_segments: u16,
|
||||
pub message_segment_num: u16
|
||||
|
||||
}
|
||||
|
||||
pub enum Message {
|
||||
|
||||
}
|
||||
|
||||
pub const LEGACY_TCM_HEADER_LENGTH: i64 = 12;
|
||||
|
||||
pub fn parse_nx2_chunk(cursor: &mut (impl Read + Seek)) -> Result<Nexrad2Chunk, NexradParseError> {
|
||||
let mut volume_header = [0u8; 24];
|
||||
|
@ -141,7 +122,7 @@ pub fn parse_nx2_chunk(cursor: &mut (impl Read + Seek)) -> Result<Nexrad2Chunk,
|
|||
let mut compressed_buf = vec![0u8; ldm_size];
|
||||
cursor.read_exact(&mut compressed_buf).map_err(|e| NexradParseError::LdmReadFailed(e))?;
|
||||
|
||||
let mut bz_decoder = BzDecoder::new(Cursor::new(compressed_buf));
|
||||
let mut bz_decoder = BzipDecoder::new(Cursor::new(compressed_buf));
|
||||
|
||||
let mut decompressed_buf = vec![];
|
||||
|
||||
|
@ -151,11 +132,17 @@ pub fn parse_nx2_chunk(cursor: &mut (impl Read + Seek)) -> Result<Nexrad2Chunk,
|
|||
|
||||
let mut decompressed = Cursor::new(decompressed_buf);
|
||||
|
||||
let mut messages = 0;
|
||||
|
||||
loop {
|
||||
decompressed.seek(SeekFrom::Current(LEGACY_TCM_HEADER_LENGTH)).map_err(|e| NexradParseError::TcmChunkReadFailed(e))?;
|
||||
decompressed.seek(SeekFrom::Current(LEGACY_CTM_HEADER_LEN as i64)).map_err(|e| NexradParseError::TcmChunkReadFailed(e))?;
|
||||
|
||||
let mut message_header = [0u8; MESSAGE_HEADER_SIZE];
|
||||
decompressed.read_exact(&mut message_header).map_err(|e| NexradParseError::TcmChunkReadFailed(e))?;
|
||||
match decompressed.read_exact(&mut message_header) {
|
||||
Ok(_) => (),
|
||||
Err(e) if matches!(e.kind(), io::ErrorKind::UnexpectedEof) => { break; },
|
||||
Err(e) => return Err(NexradParseError::TcmChunkReadFailed(e))
|
||||
}
|
||||
|
||||
let message_header = MessageHeader {
|
||||
message_size: u16::from_be_bytes(message_header[0..2].try_into().unwrap()),
|
||||
|
@ -170,9 +157,15 @@ pub fn parse_nx2_chunk(cursor: &mut (impl Read + Seek)) -> Result<Nexrad2Chunk,
|
|||
|
||||
debug!("Message: {:#?}", message_header);
|
||||
|
||||
break;
|
||||
let mut body_buf = vec![0u8; MESSAGE_BODY_SIZE];
|
||||
|
||||
decompressed.read_exact(&mut body_buf).map_err(|e| NexradParseError::TcmChunkReadFailed(e))?;
|
||||
|
||||
messages += 1;
|
||||
}
|
||||
|
||||
debug!("{} messages loaded from chunk", messages);
|
||||
|
||||
break;
|
||||
}
|
||||
|
||||
|
|
|
@ -1,3 +1,34 @@
|
|||
pub const MSG_RDA_STATUS_DATA: u8 = 2;
|
||||
pub const MSG_RDA_PERFORMANCE_DATA: u8 = 3;
|
||||
pub const MSG_RDA_VOLUME_COVERAGE_DATA: u8 = 5;
|
||||
pub const MSG_RDA_CLUTTER_FILTER_BYPASS_MAP: u8 = 13;
|
||||
pub const MSG_RDA_CLUTTER_MAP_DATA: u8 = 15;
|
||||
pub const MSG_RDA_ADAPTABLE_PARAMETERS: u8 = 18;
|
||||
pub const MSG_MODEL_DATA_MESSAGE: u8 = 29;
|
||||
pub const MSG_DIGITAL_RADAR_DATA_GENERIC: u8 = 31;
|
||||
|
||||
pub const LEGACY_CTM_HEADER_LEN: usize = 12;
|
||||
pub const MESSAGE_HEADER_SIZE: usize = 16;
|
||||
pub const DEFAULT_MESSAGE_SIZE: usize = 2432;
|
||||
pub const MESSAGE_BODY_SIZE: usize = DEFAULT_MESSAGE_SIZE - LEGACY_CTM_HEADER_LEN - MESSAGE_HEADER_SIZE;
|
||||
|
||||
#[derive(Debug)]
|
||||
pub struct MessageHeader {
|
||||
pub message_size: u16,
|
||||
pub rda_redundant_channel: u8,
|
||||
pub message_type: u8,
|
||||
pub message_sequence_number: u16,
|
||||
pub julian_date: u16,
|
||||
pub millis_after_midnight: u32,
|
||||
pub num_of_message_segments: u16,
|
||||
pub message_segment_num: u16
|
||||
|
||||
}
|
||||
|
||||
pub enum Message {
|
||||
/// Type02
|
||||
RDAStatusData {
|
||||
|
||||
},
|
||||
|
||||
}
|
Loading…
Reference in New Issue