Skip to main content

dmap/
lib.rs

1//! A library for SuperDARN DMAP file I/O.
2//!
3//! [![github]](https://github.com/SuperDARNCanada/dmap) [![crates-io]](https://crates.io/crates/darn-dmap) [![docs-rs]](crate)
4//!
5//! [github]: https://img.shields.io/badge/github-8da0cb?style=for-the-badge&labelColor=555555&logo=github
6//! [crates-io]: https://img.shields.io/badge/crates.io-fc8d62?style=for-the-badge&labelColor=555555&logo=rust
7//! [docs-rs]: https://img.shields.io/badge/docs.rs-66c2a5?style=for-the-badge&labelColor=555555&logo=docs.rs
8//!
9//! <br>
10//!
11//! This library also has a Python API using pyo3.
12//!
13//! For more information about DMAP files, see [RST](https://radar-software-toolkit-rst.readthedocs.io/en/latest/)
14//! or [pyDARNio](https://pydarnio.readthedocs.io/en/latest/).
15//!
16//! The main feature of this crate is the [`Record`] trait, which defines a valid DMAP record and functions for
17//! reading from and writing to byte streams. The SuperDARN file formats IQDAT, RAWACF, FITACF, GRID, MAP, and SND are
18//! all supported with structs that implement [`Record`], namely:
19//!
20//! - [`IqdatRecord`]
21//! - [`RawacfRecord`]
22//! - [`FitacfRecord`]
23//! - [`GridRecord`]
24//! - [`MapRecord`]
25//! - [`SndRecord`]
26//!
27//! Each struct has a list of required and optional fields that it uses to verify the integrity of the record.
28//! Only fields listed in the required and optional lists are allowed, no required field can be missing, and
29//! each field has an expected primitive type. Additionally, each format has groupings of vector fields which
30//! must all share the same shape; e.g. `acfd` and `xcfd` in a RAWACF file.
31//!
32//! There is also a generic [`DmapRecord`] struct which has no knowledge of required or optional fields. When reading from
33//! a byte stream, the parsed data will be identical when using both a specific format like [`RawacfRecord`] and the generic
34//! [`DmapRecord`]; however, when writing to a byte stream, the output may differ. Since [`DmapRecord`] has no knowledge of
35//! the expected primitive type for each field, it defaults to a type that fits the data. For example, the `stid` field may
36//! be saved as an `i8` when using [`DmapRecord`] instead of an `i16` which [`RawacfRecord`] specifies it must be.
37//!
38//! <div class="note">
39//! Each type of record has a specific field ordering hard-coded by this library. This is the order in which fields are written to file,
40//! and may not match the ordering of fields generated by RST. This also means that round-trip I/O (i.e. reading a file and
41//! writing back out to a new file) is not guaranteed to generate an identical file; however, it is guaranteed that all the
42//! information is the same, just not necessarily in the same order.
43//! </div>
44//!
45//! <br>
46//!
47//! # Examples
48//!
49//! Convenience functions for reading from and writing to a file exist to simplify the most common use cases.
50//! This is defined by [`Record::read_file`]
51//! ```
52//! use dmap::*;
53//! use std::path::PathBuf;
54//!
55//! # fn main() -> Result<(), DmapError> {
56//! let path = PathBuf::from("tests/test_files/test.rawacf");
57//! let rawacf_data = RawacfRecord::read_file(&path)?;
58//! let unchecked_data = DmapRecord::read_file(&path)?;
59//!
60//! assert_eq!(rawacf_data.len(), unchecked_data.len());
61//! assert_eq!(rawacf_data[0].get(&"stid".to_string()), unchecked_data[0].get(&"stid".to_string()));
62//!
63//! // Write the records to a file
64//! let out_path = PathBuf::from("tests/test_files/output.rawacf");
65//! RawacfRecord::write_to_file(&rawacf_data, &out_path, false)?;
66//! # std::fs::remove_file(out_path)?;
67//! #    Ok(())
68//! # }
69//! ```
70//! You can read from anything that implements the `Read` trait using the functions exposed by the [`Record`] trait.
71//! Detection and decompression of bz2 is also conducted automatically.
72//! ```
73//! use dmap::*;
74//! use std::fs::File;
75//! use itertools::izip;
76//!
77//! # fn main() -> Result<(), DmapError> {
78//! let file = File::open("tests/test_files/test.rawacf.bz2")?;  // `File` implements the `Read` trait
79//! let rawacf_data = RawacfRecord::read_records(file)?;
80//!
81//! let uncompressed_data = RawacfRecord::read_file("tests/test_files/test.rawacf")?;
82//! assert_eq!(rawacf_data.len(), uncompressed_data.len());
83//! for (left, right) in izip!(rawacf_data, uncompressed_data) {
84//!     assert_eq!(left, right)
85//! }
86//! #     Ok(())
87//! # }
88//! ```
89
90pub(crate) mod compression;
91pub(crate) mod convenience;
92pub mod error;
93pub mod formats;
94pub(crate) mod io;
95mod parser;
96pub mod record;
97pub mod types;
98
99#[cfg(feature = "python")]
100mod python_types;
101
102#[cfg(feature = "python")]
103mod python_bindings;
104
105pub use crate::error::DmapError;
106pub use crate::formats::dmap::DmapRecord;
107pub use crate::formats::fitacf::FitacfRecord;
108pub use crate::formats::grid::GridRecord;
109pub use crate::formats::iqdat::IqdatRecord;
110pub use crate::formats::map::MapRecord;
111pub use crate::formats::rawacf::RawacfRecord;
112pub use crate::formats::snd::SndRecord;
113pub use crate::record::Record;