///|
pub enum ValidationLevel {
Info
Warning
Error
} derive(Debug, Eq)
///|
pub fn all_validation_levels() -> Array[ValidationLevel] {
[Info, Warning, Error]
}
///|
pub struct ValidationMessage {
level : ValidationLevel
code : String
message : String
} derive(Debug, Eq)
///|
pub struct ValidationReport {
valid : Bool
messages : Array[ValidationMessage]
score : Double
} derive(Debug, Eq)
///|
pub fn ValidationMessage::new(
level : ValidationLevel,
code : String,
message : String,
) -> ValidationMessage {
{ level, code, message }
}
///|
pub fn validate_elements(elements : ClassicalElements) -> ValidationReport {
let messages : Array[ValidationMessage] = []
if elements.semi_major_axis_km <= 0.0 {
messages.push(
ValidationMessage::new(Error, "axis", "semi-major axis must be positive"),
)
}
if elements.eccentricity < 0.0 || elements.eccentricity >= 1.0 {
messages.push(
ValidationMessage::new(
Error,
"eccentricity",
"elliptic eccentricity must be in [0,1)",
),
)
}
if elements.inclination_rad.is_nan() {
messages.push(
ValidationMessage::new(Error, "inclination", "inclination is not finite"),
)
}
{
valid: messages.filter(item => item.level is Error).length() == 0,
score: 1.0 - Double::from_int(messages.length()) * 0.2,
messages,
}
}
///|
pub fn validate_state(state : StateVector) -> ValidationReport {
let messages : Array[ValidationMessage] = []
if state.position_km.norm() == 0.0 {
messages.push(
ValidationMessage::new(Error, "position", "position cannot be zero"),
)
}
if state.velocity_km_s.norm() > 100.0 {
messages.push(
ValidationMessage::new(
Warning,
"speed",
"speed exceeds nominal mission range",
),
)
}
{
valid: messages.filter(item => item.level is Error).length() == 0,
score: 1.0 - Double::from_int(messages.length()) * 0.2,
messages,
}
}
///|
pub fn validate_site(site : Geodetic) -> ValidationReport {
let messages : Array[ValidationMessage] = []
if site.latitude_rad.abs() > half_pi {
messages.push(
ValidationMessage::new(
Error,
"latitude",
"latitude outside physical range",
),
)
}
if site.altitude_km < -earth_radius_km {
messages.push(
ValidationMessage::new(Error, "altitude", "site is below body center"),
)
}
{
valid: messages.length() == 0,
score: 1.0 - Double::from_int(messages.length()) * 0.2,
messages,
}
}
///|
pub fn validate_timeline(timeline : MissionTimeline) -> ValidationReport {
let messages : Array[ValidationMessage] = []
for event in timeline.events {
if event.time_s < 0.0 {
messages.push(
ValidationMessage::new(Error, "event-time", "event is before epoch"),
)
}
if event.duration_s < 0.0 {
messages.push(
ValidationMessage::new(Error, "duration", "duration cannot be negative"),
)
}
}
{
valid: messages.length() == 0,
score: 1.0 - Double::from_int(messages.length()) * 0.2,
messages,
}
}
///|
pub fn validate_telemetry(samples : Array[TelemetrySample]) -> ValidationReport {
let messages : Array[ValidationMessage] = []
if samples.length() == 0 {
messages.push(
ValidationMessage::new(Warning, "empty", "telemetry stream is empty"),
)
}
if samples.any(sample => sample.quality is Invalid) {
messages.push(
ValidationMessage::new(
Error,
"invalid",
"telemetry contains invalid values",
),
)
}
{
valid: messages.filter(item => item.level is Error).length() == 0,
score: 1.0 - Double::from_int(messages.length()) * 0.2,
messages,
}
}
///|
pub fn validation_error_count(report : ValidationReport) -> Int {
report.messages.filter(item => item.level is Error).length()
}
///|
pub fn validation_warning_count(report : ValidationReport) -> Int {
report.messages.filter(item => item.level is Warning).length()
}
///|
pub fn validation_summary(report : ValidationReport) -> String {
"valid=\{report.valid},score=\{report.score},messages=\{report.messages.length()}"
}