evict f64's from the kernel, and eliminate f32 parsing

turns out these routines add quite a bit of bloat to the kernel
for only cosmetic reasons.
This commit is contained in:
bunnie 2022-06-14 04:14:25 +08:00
parent 0abca42298
commit 7b2fbdc88b
13 changed files with 161 additions and 65 deletions

View file

@ -30,6 +30,37 @@ const STOP_ID: usize = 1;
const SAMPLE_RATE_HZ: f32 = 8000.0;
// note to self: A4 = 440.0, E4 = 329.63, C4 = 261.63
use std::num::ParseIntError;
/// this will parse a simple decimal into an i32, multiplied by 1000
/// we do this because the full f32 parsing stuff is pretty heavy, some
/// 28kiB of code
#[inline(never)]
fn simple_kilofloat_parse(input: &str) -> core::result::Result<i32, ParseIntError> {
if let Some((integer, fraction)) = input.split_once('.') {
let mut result = integer.parse::<i32>()? * 1000;
let mut significance = 100i32;
for (place, digit) in fraction.chars().enumerate() {
if place >= 3 {
break;
}
if let Some(d) = digit.to_digit(10) {
if result >= 0 {
result += (d as i32) * significance;
} else {
result -= (d as i32) * significance;
}
significance /= 10;
} else {
return "z".parse::<i32>() // you can't create a ParseIntError any other way
}
}
Ok(result)
} else {
let base = input.parse::<i32>()?;
Ok(base * 1000)
}
}
impl<'a> ShellCmdApi<'a> for Audio {
cmd_api!(audio);
@ -42,15 +73,15 @@ impl<'a> ShellCmdApi<'a> for Audio {
match sub_cmd {
"tone" => {
self.freq = if let Some(freq_str) = tokens.next() {
match freq_str.parse::<f32>() {
Ok(f) => f,
match simple_kilofloat_parse(freq_str) {
Ok(f) => (f as f32) / 1000.0,
Err(_) => 440.0,
}
} else {
440.0
};
let mut duration: f32 = if let Some(duration_str) = tokens.next() {
duration_str.parse::<f32>().unwrap_or(0.5)
simple_kilofloat_parse(duration_str).unwrap_or(500) as f32 / 1000.0
} else {
0.5
};

View file

@ -578,9 +578,9 @@ fn main() -> ! {
llio.boost_on(false).unwrap();
ticktimer.sleep_ms(50).unwrap(); // give some time for the voltage to move
let vbus = (llio.adc_vbus().unwrap() as f64) * 0.005033;
log::info!("Vbus is: {:.3}V", vbus);
if vbus > 1.5 {
let vbus = (llio.adc_vbus().unwrap() as u32) * 503;
log::info!("Vbus is: {}mV", vbus / 100);
if vbus > 150_000 { // 1.5V
// if power is plugged in, request that it be removed
modals.show_notification(t!("rootkeys.init.unplug_power", xous::LANG), None).expect("modals error");
log::info!("vbus is high, holding off on reboot");

View file

@ -343,7 +343,7 @@ impl<'a> ShellCmdApi<'a> for Aes {
xous::msg_scalar_unpack!(msg, pass, hw_mode, keybits, _, {
let end = env.ticktimer.elapsed_ms();
let elapsed: f64 = ((end - self.start_time.unwrap()) as f64) / (TEST_ITERS as f64 * (TEST_MAX_LEN / aes::BLOCK_SIZE) as f64);
let elapsed: f32= ((end - self.start_time.unwrap()) as f32) / (TEST_ITERS as f32 * (TEST_MAX_LEN / aes::BLOCK_SIZE) as f32);
let modestr = if hw_mode != 0 { &"hw" } else { &"sw" };
if pass != 0 {
write!(ret, "[{}] passed: {:.02}µs/block enc+dec AES{}", modestr, elapsed * 1000.0, keybits).unwrap();

View file

@ -127,7 +127,7 @@ fn do_update(com: &mut com::Com, callback_conn: xous::CID, package: &[u8], pkg_o
prog_addr += 1024;
progress_ctr += 1;
if (progress_ctr % 12) == 0 {
write!(update_str, "{:.0}% complete", ((prog_addr - flash_start) as f64 / image_len as f64) * 100.0).unwrap();
write!(update_str, "{}% complete", ((prog_addr - flash_start) * 100) / image_len).unwrap();
Buffer::into_buf(update_str).unwrap().lend(callback_conn, CB_ID.load(Ordering::Relaxed)).unwrap();
update_str.clear();
}
@ -480,7 +480,7 @@ impl<'a> ShellCmdApi<'a> for EcUpdate {
write!(ret, "Starting EC wf200 update").unwrap();
}
"auto" => {
if ((env.llio.adc_vbus().unwrap() as f64) * 0.005033) > 1.5 {
if ((env.llio.adc_vbus().unwrap() as u32) * 503) > 150_000 { // 1.5V
// if power is plugged in, deny powerdown request
write!(ret, "Can't EC auto update while charging. Unplug charging cable and try again.").unwrap();
return Ok(Some(ret));
@ -523,13 +523,13 @@ impl<'a> ShellCmdApi<'a> for EcUpdate {
} else { // otherwise, unpack it and use the first argument as a sub-opcode type
xous::msg_scalar_unpack!(msg, result_code, progress, _, _, {
let end = env.ticktimer.elapsed_ms();
let elapsed: f64 = (end - self.start_time.unwrap()) as f64;
let elapsed = end - self.start_time.unwrap();
match FromPrimitive::from_usize(result_code) {
Some(UpdateResult::PackageValid) => {
write!(ret, "Firmware package validated in {}ms", elapsed).unwrap();
},
Some(UpdateResult::ProgramDone) => {
write!(ret, "Programming of {} bytes done in {:.1}s. Please restart EC with `ecup reset`.", progress, elapsed / 1000.0).unwrap();
write!(ret, "Programming of {} bytes done in {}s. Please restart EC with `ecup reset`.", progress, elapsed / 1000).unwrap();
env.netmgr.connection_manager_run().unwrap();
self.in_progress = false;
},
@ -539,7 +539,7 @@ impl<'a> ShellCmdApi<'a> for EcUpdate {
self.in_progress = false;
},
Some(UpdateResult::Abort) => {
write!(ret, "Programming aborted in {:.1}s. Did you stage all the firmware objects?", elapsed / 1000.0).unwrap();
write!(ret, "Programming aborted in {}s. Did you stage all the firmware objects?", elapsed / 1000).unwrap();
env.netmgr.connection_manager_run().unwrap();
self.in_progress = false;
}

View file

@ -549,7 +549,7 @@ impl<'a> ShellCmdApi<'a> for Engine {
xous::msg_scalar_unpack!(msg, passes, fails, result_type, iters, {
let end = env.ticktimer.elapsed_ms();
let elapsed: f64 = ((end - self.start_time.unwrap()) as f64) / iters as f64;
let elapsed: f32 = ((end - self.start_time.unwrap()) as f32) / iters as f32;
match FromPrimitive::from_usize(result_type) {
Some(BenchResult::EngineDone) => {
write!(ret, "{}ms/check_iter; In total, Engine passed {} vectors, failed {} vectors", elapsed, passes, fails).unwrap();

View file

@ -191,7 +191,7 @@ impl<'a> ShellCmdApi<'a> for Memtest {
xous::msg_scalar_unpack!(msg, errs, iters, dummy, _, {
let end = env.ticktimer.elapsed_ms();
let elapsed: f64 = ((end - self.start_time.unwrap()) as f64) / iters as f64;
let elapsed: f32 = ((end - self.start_time.unwrap()) as f32) / iters as f32;
write!(ret, "memtest finished: {} errs, {}ms/iter", errs, elapsed).unwrap();
log::info!("dummy var: {}", dummy); // just make sure it's not optimized out for any reason!
});

View file

@ -18,14 +18,15 @@ impl<'a> ShellCmdApi<'a> for Sensors {
use core::fmt::Write;
let mut ret = String::<1024>::new();
write!(ret, "Vbus {:.2}V\nVint {:.2}V\nVaux {:.2}V\nVbram {:.2}V\nUSB {:.2}|{:.2}V\nTemp {:.1}°C",
(env.llio.adc_vbus().unwrap() as f64) * 0.005033,
(env.llio.adc_vccint().unwrap() as f64) / 1365.0,
(env.llio.adc_vccaux().unwrap() as f64) / 1365.0,
(env.llio.adc_vccbram().unwrap() as f64) / 1365.0,
(env.llio.adc_usb_p().unwrap() as f64) / 1365.0,
(env.llio.adc_usb_n().unwrap() as f64) / 1365.0,
((env.llio.adc_temperature().unwrap() as f64) * 0.12304) - 273.15,
let milli_celcius = (((env.llio.adc_temperature().unwrap() as u32) * 12304) - 27_315_000) / 100;
write!(ret, "Vbus {:.2}mV\nVint {:.2}mV\nVaux {:.2}mV\nVbram {:.2}mV\nUSB {:.2}|{:.2}mV\nTemp {}.{}°C",
((env.llio.adc_vbus().unwrap() as u32) * 503) / 100,
((env.llio.adc_vccint().unwrap() as u32) * 1000) / 1365,
((env.llio.adc_vccaux().unwrap() as u32) * 1000) / 1365,
((env.llio.adc_vccbram().unwrap() as u32) * 1000) / 1365,
((env.llio.adc_usb_p().unwrap() as u32) * 1000) / 1365,
((env.llio.adc_usb_n().unwrap() as u32) * 1000) / 1365,
milli_celcius / 1000, (milli_celcius % 1000) / 10
).unwrap();
Ok(Some(ret))

View file

@ -254,7 +254,7 @@ impl<'a> ShellCmdApi<'a> for Sha {
xous::msg_scalar_unpack!(msg, pass, first_time, hw_mode, _, {
let end = env.ticktimer.elapsed_ms();
let elapsed: f64 = ((end - self.start_time.unwrap()) as f64) / TEST_ITERS as f64;
let elapsed: f32 = ((end - self.start_time.unwrap()) as f32) / TEST_ITERS as f32;
let modestr = if hw_mode != 0 { &"hw" } else { &"sw" };
if first_time != 0 {
write!(ret, "[{}] first pass: {}ms/hash", modestr, elapsed).unwrap();

View file

@ -91,7 +91,7 @@ impl<'a> ShellCmdApi<'a> for Sleep {
write!(ret, "Starting suspend/resume stress test. Hard reboot required to exit.").unwrap();
}
"now" => {
if ((env.llio.adc_vbus().unwrap() as f64) * 0.005033) > 1.5 {
if ((env.llio.adc_vbus().unwrap() as u32) * 503) > 150_000 { // 1.5V
// if power is plugged in, deny powerdown request
write!(ret, "System can't sleep while charging. Unplug charging cable and try again.").unwrap();
} else {
@ -121,7 +121,7 @@ impl<'a> ShellCmdApi<'a> for Sleep {
}
}
"ship" => {
if ((env.llio.adc_vbus().unwrap() as f64) * 0.005033) > 1.5 {
if ((env.llio.adc_vbus().unwrap() as u32) * 503) > 150_000 { // 1.5V
// if power is plugged in, deny powerdown request
write!(ret, "System can't go into ship mode while charging. Unplug charging cable and try again.").unwrap();
} else {
@ -147,7 +147,7 @@ impl<'a> ShellCmdApi<'a> for Sleep {
}
}
"coldboot" => {
if ((env.llio.adc_vbus().unwrap() as f64) * 0.005033) > 1.5 {
if ((env.llio.adc_vbus().unwrap() as u32) * 503) > 150_000 {
// if power is plugged in, deny powerdown request
write!(ret, "System can't cold boot while charging. Unplug charging cable and try again.").unwrap();
} else {
@ -177,7 +177,7 @@ impl<'a> ShellCmdApi<'a> for Sleep {
xous::create_thread_1(kill_thread, 0).unwrap();
}
"killbounce" => {
if ((env.llio.adc_vbus().unwrap() as f64) * 0.005033) > 1.5 {
if ((env.llio.adc_vbus().unwrap() as u32) * 503) > 150_000 {
// if power is plugged in, deny powerdown request
write!(ret, "Unplug charging cable and try again.").unwrap();
} else {

View file

@ -91,6 +91,36 @@ enum TestOp {
ModalDrop,
}
use std::num::ParseIntError;
/// this will parse a simple decimal into an i32, multiplied by 1000
/// we do this because the full f32 parsing stuff is pretty heavy, some
/// 28kiB of code
#[inline(never)]
fn simple_kilofloat_parse(input: &str) -> core::result::Result<i32, ParseIntError> {
if let Some((integer, fraction)) = input.split_once('.') {
let mut result = integer.parse::<i32>()? * 1000;
let mut significance = 100i32;
for (place, digit) in fraction.chars().enumerate() {
if place >= 3 {
break;
}
if let Some(d) = digit.to_digit(10) {
if result >= 0 {
result += (d as i32) * significance;
} else {
result -= (d as i32) * significance;
}
significance /= 10;
} else {
return "z".parse::<i32>() // you can't create a ParseIntError any other way
}
}
Ok(result)
} else {
let base = input.parse::<i32>()?;
Ok(base * 1000)
}
}
impl<'a> ShellCmdApi<'a> for Test {
cmd_api!(test);
@ -166,8 +196,8 @@ impl<'a> ShellCmdApi<'a> for Test {
let mut ht = env.trng.get_health_tests().unwrap();
for _ in 0..3 { // run the test 3 times
av_excurs = [
(((ht.av_excursion[0].max as f64 - ht.av_excursion[0].min as f64) / 4096.0) * 1000.0) as u32,
(((ht.av_excursion[1].max as f64 - ht.av_excursion[1].min as f64) / 4096.0) * 1000.0) as u32,
(((ht.av_excursion[0].max as f32 - ht.av_excursion[0].min as f32) / 4096.0) * 1000.0) as u32,
(((ht.av_excursion[1].max as f32 - ht.av_excursion[1].min as f32) / 4096.0) * 1000.0) as u32,
];
// 78mv minimum excursion requirement for good entropy generation
if av_excurs[0] < 78 { av_pass[0] = false; }
@ -310,8 +340,8 @@ impl<'a> ShellCmdApi<'a> for Test {
}
"astart" => {
self.freq = if let Some(freq_str) = tokens.next() {
match freq_str.parse::<f32>() {
Ok(f) => f,
match simple_kilofloat_parse(freq_str) {
Ok(f) => (f as f32) / 1000.0,
Err(_) => 440.0,
}
} else {
@ -400,7 +430,7 @@ impl<'a> ShellCmdApi<'a> for Test {
log::info!("{}|ASTOP|", SENTINEL);
}
"oqc" => {
if ((env.llio.adc_vbus().unwrap() as f64) * 0.005033) > 1.5 {
if ((env.llio.adc_vbus().unwrap() as u32) * 503) > 150_000 { // 0.005033 * 100_000 against 1.5V * 100_000
// if power is plugged in, deny powerdown request
write!(ret, "Can't run OQC test while charging. Unplug charging cable and try again.").unwrap();
return Ok(Some(ret));
@ -583,7 +613,7 @@ impl<'a> ShellCmdApi<'a> for Test {
write!(ret, "devboot off").unwrap();
}
"ship" => {
if ((env.llio.adc_vbus().unwrap() as f64) * 0.005033) > 1.5 {
if ((env.llio.adc_vbus().unwrap() as u32) * 503) > 150_000 { // 0.005033 * 100_000 against 1.5V * 100_000
// if power is plugged in, deny powerdown request
write!(ret, "System can't go into ship mode while charging. Unplug charging cable and try again.").unwrap();
} else {

View file

@ -42,19 +42,19 @@ impl<'a> ShellCmdApi<'a> for TrngCmd {
}
"excur" => {
let ht = env.trng.get_health_tests().unwrap();
write!(ret, "AV0: {:.0}/{:.0} mV\n"
,(ht.av_excursion[0].min as f64 / 4096.0) * 1000.0
,(ht.av_excursion[0].max as f64 / 4096.0) * 1000.0
write!(ret, "AV0: {}/{} mV\n"
,((ht.av_excursion[0].min as u32 * 1000) / 4096)
,((ht.av_excursion[0].max as u32 * 1000) / 4096)
).unwrap();
write!(ret, "AV0 delta: {:.0} mV\n"
,((ht.av_excursion[0].max as f64 - ht.av_excursion[0].min as f64) / 4096.0) * 1000.0
write!(ret, "AV0 delta: {} mV\n"
,(((ht.av_excursion[0].max as u32 - ht.av_excursion[0].min as u32) * 1000) / 4096)
).unwrap();
write!(ret, "AV1: {:.0}/{:.0} mV\n"
,(ht.av_excursion[1].min as f64 / 4096.0) * 1000.0
,(ht.av_excursion[1].max as f64 / 4096.0) * 1000.0
write!(ret, "AV1: {}/{} mV\n"
,((ht.av_excursion[1].min as u32 * 1000) / 4096)
,((ht.av_excursion[1].max as u32 * 1000) / 4096)
).unwrap();
write!(ret, "AV1 delta: {:.0} mV\n"
,((ht.av_excursion[1].max as f64 - ht.av_excursion[1].min as f64) / 4096.0) * 1000.0
write!(ret, "AV1 delta: {} mV\n"
,(((ht.av_excursion[1].max as u32 - ht.av_excursion[1].min as u32) * 1000 ) / 4096)
).unwrap();
}
"pump" => {

View file

@ -377,17 +377,21 @@ fn main() -> ! {
write!(&mut battstats_tv, "{}", t!("stats.measuring", xous::LANG)).unwrap();
} else {
// toggle between two views of the data every time we have a status update
let mut wattage = stats.current as f32 / 1000.0 * stats.voltage as f32 / 1000.0;
let sign = if wattage > 0.005 {
let mut wattage_mw = (stats.current as i32 * stats.voltage as i32) / 1000i32;
let sign = if wattage_mw > 5 {
'\u{2b06}' // up arrow
} else if wattage < -0.005 {
} else if wattage_mw < -5 {
'\u{2b07}' // down arrow
} else {
'\u{1f50c}' // plugged in icon (e.g., fully charged, running on wall power now)
};
wattage = wattage.abs();
wattage_mw = wattage_mw.abs();
if battstats_phase {
write!(&mut battstats_tv, "{:.3}W{}{:.2}V {}%", wattage, sign, stats.voltage as f32 / 1000.0, stats.soc).unwrap();
write!(&mut battstats_tv, "{}.{}W{}{}.{}V {}%",
wattage_mw / 1000, wattage_mw % 1000,
sign,
stats.voltage as u32 / 1000, (stats.voltage as u32 % 1000) / 10, // 2 decimal places
stats.soc).unwrap();
} else {
if let Some(ssid) = wifi_status.ssid {
write!(
@ -431,7 +435,7 @@ fn main() -> ! {
let (latest_activity, period) = llio
.activity_instantaneous()
.expect("couldn't get CPU activity");
let activity_to_width = ((latest_activity as f32) / (period as f32)) * (cpuload_rect.width() - 4) as f32;
let activity_to_width = (((latest_activity * 1000) / (period)) * (cpuload_rect.width() - 4) as u32) / 1000;
draw_list.push(GamObjectType::Rect(
Rectangle::new_coords_with_style(
cpuload_rect.tl().x + 2,
@ -503,7 +507,7 @@ fn main() -> ! {
// confirm that the charger is in the right state.
if stats.soc < 95 || stats.remaining_capacity < 1000 {
// only request if we aren't fully charged, either by SOC or capacity metrics
if (llio.adc_vbus().unwrap() as f64) * 0.005033 > 4.45 {
if (llio.adc_vbus().unwrap() as u32) * 503 > 445_000 { // 0.005033 * 100_000 against 4.45V * 100_000
// 4.45V is our threshold for deciding if a cable is present
// charging cable is present
if !com.is_charging().expect("couldn't check charging state") {
@ -575,7 +579,7 @@ fn main() -> ! {
stats_phase = stats_phase.wrapping_add(1);
}
Some(StatusOpcode::Reboot) => {
if ((llio.adc_vbus().unwrap() as f64) * 0.005033) > 1.5 {
if ((llio.adc_vbus().unwrap() as u32) * 503) > 150_000 { // 0.005033 * 100_000 against 1.5V * 100_000
// power plugged in, do a reboot using a warm boot method
susres.reboot(true).expect("couldn't issue reboot command");
} else {
@ -638,14 +642,14 @@ fn main() -> ! {
).expect("couldn't trigger status update");
}),
Some(StatusOpcode::TrySuspend) => {
if ((llio.adc_vbus().unwrap() as f64) * 0.005033) > 1.5 {
if ((llio.adc_vbus().unwrap() as u32) * 503) > 150_000 {
modals.show_notification(t!("mainmenu.cant_sleep", xous::LANG), None).expect("couldn't notify that power is plugged in");
} else {
susres.initiate_suspend().expect("couldn't initiate suspend op");
}
},
Some(StatusOpcode::BatteryDisconnect) => {
if ((llio.adc_vbus().unwrap() as f64) * 0.005033) > 1.5 {
if ((llio.adc_vbus().unwrap() as u32) * 503) > 150_000 {
modals.show_notification(t!("mainmenu.cant_sleep", xous::LANG), None).expect("couldn't notify that power is plugged in");
} else {
gam.shipmode_blank_request().ok();

View file

@ -36,6 +36,7 @@ use gam::modal::*;
// ntp imports
use sntpc::{Error, NtpContext, NtpTimestampGenerator, NtpUdpSocket, Result};
use std::net::{SocketAddr, ToSocketAddrs, UdpSocket};
use std::num::ParseIntError;
/// This is a "well known name" used by `libstd` to connect to the time server
/// Even thought it is "public" nobody connects to it directly, they connect to it via `libstd`
@ -478,6 +479,36 @@ fn to_bcd(binary: u8) -> u8 {
(msd << 4) | lsd
}
/// this will parse a simple decimal into an i32, multiplied by 1000
/// we do this because the full f32 parsing stuff is pretty heavy, some
/// 28kiB of code
#[inline(never)]
fn simple_kilofloat_parse(input: &str) -> core::result::Result<i32, ParseIntError> {
if let Some((integer, fraction)) = input.split_once('.') {
let mut result = integer.parse::<i32>()? * 1000;
let mut significance = 100i32;
for (place, digit) in fraction.chars().enumerate() {
if place >= 3 {
break;
}
if let Some(d) = digit.to_digit(10) {
if result >= 0 {
result += (d as i32) * significance;
} else {
result -= (d as i32) * significance;
}
significance /= 10;
} else {
return "z".parse::<i32>() // you can't create a ParseIntError any other way
}
}
Ok(result)
} else {
let base = input.parse::<i32>()?;
Ok(base * 1000)
}
}
pub fn start_time_ux(sid: xous::SID) {
thread::spawn({
move || {
@ -517,15 +548,14 @@ pub fn start_time_ux(sid: xous::SID) {
// a key exists, but nothing was written to it (length of key was 0 or inappropriate)
if !tz_set {
log::info!("{}RTC.TZ,{}", xous::BOOKEND_START, xous::BOOKEND_END);
let tz = modals.alert_builder(t!("rtc.timezone", xous::LANG))
let tz_str = modals.alert_builder(t!("rtc.timezone", xous::LANG))
.field(None, Some(tz_ux_validator))
.build()
.expect("couldn't get timezone")
.first()
.as_str()
.parse::<f32>().expect("pre-validated input failed to re-parse!");
.first();
let tz = simple_kilofloat_parse(tz_str.as_str()).expect("pre-validated input failed to re-parse!");
log::info!("got tz offset {}", tz);
tz_offset_ms = (tz * 3600.0 * 1000.0) as i64;
tz_offset_ms = (tz * 3600) as i64;
xous::send_message(timeserver_cid,
Message::new_scalar(
crate::time::TimeOp::SetTzOffsetMs.to_usize().unwrap(),
@ -632,15 +662,15 @@ pub fn start_time_ux(sid: xous::SID) {
continue;
}
let tz = modals.alert_builder(t!("rtc.timezone", xous::LANG))
let tz_str = modals.alert_builder(t!("rtc.timezone", xous::LANG))
.field(None, Some(tz_ux_validator))
.build()
.expect("couldn't get timezone")
.first()
.as_str()
.parse::<f32>().expect("pre-validated input failed to re-parse!");
.first();
let tz = simple_kilofloat_parse(tz_str.as_str())
.expect("pre-validated input failed to re-parse!");
log::info!("got tz offset {}", tz);
let tzoff_ms = (tz * 3600.0 * 1000.0) as i64;
let tzoff_ms = (tz * 3600) as i64;
xous::send_message(timeserver_cid,
Message::new_scalar(
crate::time::TimeOp::SetTzOffsetMs.to_usize().unwrap(),
@ -678,8 +708,8 @@ pub(crate) enum ValidatorOp {
fn tz_ux_validator(input: TextEntryPayload) -> Option<ValidatorErr> {
let text_str = input.as_str();
match text_str.parse::<f32>() {
Ok(input) => if input < -12.0 || input > 14.0 {
match simple_kilofloat_parse(text_str) {
Ok(input) => if input < -12_000 || input > 14_000 {
return Some(ValidatorErr::from_str(t!("rtc.range_err", xous::LANG)));
},
_ => return Some(ValidatorErr::from_str(t!("rtc.integer_err", xous::LANG))),