diff --git a/src/attiny_rtc_i2c.rs b/src/attiny_rtc_i2c.rs index a7c4619..2e4f75f 100644 --- a/src/attiny_rtc_i2c.rs +++ b/src/attiny_rtc_i2c.rs @@ -36,6 +36,7 @@ pub mod tc2_agent_state { pub const TAKE_AUDIO: u8 = 1 << 4; pub const OFFLOAD: u8 = 1 << 5; pub const THERMAL_MODE: u8 = 1 << 6; + pub const LONG_AUDIO_RECORDING: u8 = 1 << 7; } #[repr(u8)] @@ -72,6 +73,16 @@ impl From for CameraState { } } +#[repr(u8)] +#[derive(Format)] + +pub enum RecordingType { + TestRecording = 0, + LongRecording = 1, + ScheduledRecording = 2, + ThermalRequestedScheduledRecording = 3, +} + #[repr(u8)] enum CameraConnectionState { NoConnection = 0x00, @@ -475,14 +486,28 @@ impl SharedI2C { self.try_attiny_read_command(REG_TC2_AGENT_STATE, delay, None) } - pub fn tc2_agent_requested_test_audio_rec(&mut self, delay: &mut Delay) -> Result { + pub fn tc2_agent_requested_audio_recording( + &mut self, + delay: &mut Delay, + ) -> Result, Error> { match self.try_attiny_read_command(REG_TC2_AGENT_STATE, delay, None) { Ok(state) => { - let rec_state: bool = (state & tc2_agent_state::READY - == state & tc2_agent_state::READY) - && (state & tc2_agent_state::TEST_AUDIO_RECORDING - == tc2_agent_state::TEST_AUDIO_RECORDING); - Ok(rec_state) + let rec_state: bool = + state & tc2_agent_state::READY == state & tc2_agent_state::READY; + if rec_state { + if state & tc2_agent_state::TEST_AUDIO_RECORDING + == tc2_agent_state::TEST_AUDIO_RECORDING + { + return Ok(Some(RecordingType::TestRecording)); + } else if state & tc2_agent_state::LONG_AUDIO_RECORDING + == tc2_agent_state::LONG_AUDIO_RECORDING + { + return Ok(Some(RecordingType::LongRecording)); + } else if state & tc2_agent_state::TAKE_AUDIO == tc2_agent_state::TAKE_AUDIO { + return Ok(Some(RecordingType::ThermalRequestedScheduledRecording)); + } + } + Ok(None) } Err(e) => Err(e), } @@ -514,11 +539,15 @@ impl SharedI2C { &mut self, delay: &mut Delay, clear_flag: u8, - set_flag: u8, + set_flag: Option, ) -> Result<(), Error> { match self.try_attiny_read_command(REG_TC2_AGENT_STATE, delay, None) { Ok(state) => { - let val = (state & !clear_flag) | set_flag; + let mut val = state & !clear_flag; + if let Some(flag) = set_flag { + val = val | flag; + } + match self.try_attiny_write_command(REG_TC2_AGENT_STATE, val, delay) { Ok(_) => Ok(()), Err(x) => Err(x), @@ -544,12 +573,6 @@ impl SharedI2C { self.tc2_agent_write_flag(delay, tc2_agent_state::THERMAL_MODE, false) } - pub fn tc2_agent_requested_audio_rec(&mut self, delay: &mut Delay) -> Result { - match self.try_attiny_read_command(REG_TC2_AGENT_STATE, delay, None) { - Ok(state) => Ok((state & tc2_agent_state::TAKE_AUDIO) == tc2_agent_state::TAKE_AUDIO), - Err(e) => Err(e), - } - } pub fn tc2_agent_take_audio_rec(&mut self, delay: &mut Delay) -> Result<(), Error> { self.tc2_agent_write_flag(delay, tc2_agent_state::TAKE_AUDIO, true) } diff --git a/src/core0_audio.rs b/src/core0_audio.rs index e45f24a..64969dd 100644 --- a/src/core0_audio.rs +++ b/src/core0_audio.rs @@ -1,4 +1,4 @@ -use crate::attiny_rtc_i2c::{tc2_agent_state, I2CConfig, SharedI2C}; +use crate::attiny_rtc_i2c::{tc2_agent_state, I2CConfig, RecordingType, SharedI2C}; use crate::bsp; use crate::bsp::pac; use crate::bsp::pac::Peripherals; @@ -169,12 +169,36 @@ pub fn audio_task( ); } - let mut take_test_rec = false; - if let Ok(test_rec) = shared_i2c.tc2_agent_requested_test_audio_rec(&mut delay) { - take_test_rec = test_rec; - } - let mut do_recording = alarm_triggered; + let mut duration = 60; + let mut recording_type = None; + let mut user_recording_requested = false; let mut thermal_requested_audio = false; + if alarm_triggered { + recording_type = Some(RecordingType::ScheduledRecording); + } else { + if let Ok(audio_rec) = shared_i2c.tc2_agent_requested_audio_recording(&mut delay) { + recording_type = audio_rec; + + if let Some(rec_type) = recording_type.as_mut() { + match rec_type { + RecordingType::LongRecording => { + duration = 60 * 5; + user_recording_requested = true; + } + RecordingType::TestRecording => { + duration = 10; + user_recording_requested = true; + } + RecordingType::ThermalRequestedScheduledRecording => { + duration = 60; + thermal_requested_audio = true; + } + _ => {} + } + } + } + } + let mut reschedule = false; let mut alarm_date_time: Option = None; @@ -204,9 +228,10 @@ pub fn audio_task( ); } - if !take_test_rec { + if !user_recording_requested { if device_config_was_updated { let reboot; + if device_config.config().is_audio_device() && !thermal_requested_audio { if let AudioMode::AudioOnly = device_config.config().audio_mode { reboot = false; @@ -349,21 +374,12 @@ pub fn audio_task( } } - if let Ok(take_rec) = shared_i2c.tc2_agent_requested_audio_rec(&mut delay) { - thermal_requested_audio = take_rec; - if thermal_requested_audio { - do_recording = true; - } - } else { - thermal_requested_audio = false; - } - info!( "Alarm triggered {} scheduled {} thermal requested {}", - alarm_triggered, scheduled, thermal_requested_audio + alarm_triggered, scheduled, recording_type ); - if !do_recording && scheduled { + if recording_type.is_none() && scheduled { // check we haven't missed the alarm somehow if let Some(alarm) = alarm_date_time { let synced = synced_date_time.get_adjusted_dt(timer); @@ -385,12 +401,12 @@ pub fn audio_task( ), &mut flash_storage, ); - do_recording = true; + recording_type = Some(RecordingType::ScheduledRecording); } } } } - if do_recording || take_test_rec { + if recording_type.is_some() { watchdog.feed(); //should of already offloaded but extra safety check if !flash_storage.is_too_full_for_audio() { @@ -408,10 +424,7 @@ pub fn audio_task( pio1, sm1, ); - let mut duration = 60; - if !do_recording && take_test_rec { - duration = 10; - } + event_logger.log_event( LoggerEvent::new( LoggerEventKind::StartedAudioRecording, @@ -454,48 +467,62 @@ pub fn audio_task( ), &mut flash_storage, ); - if take_test_rec { - info!("taken test recoridng clearing status"); - watchdog.feed(); - let _ = shared_i2c.tc2_agent_clear_and_set_flag( - &mut delay, - tc2_agent_state::TEST_AUDIO_RECORDING, - tc2_agent_state::THERMAL_MODE, - ); - - let mut peripherals: Peripherals = unsafe { Peripherals::steal() }; - - offload_flash_storage_and_events( - &mut flash_storage, - &mut pi_spi, - &mut peripherals.RESETS, - &mut peripherals.DMA, - clock_freq, - &mut shared_i2c, - &mut delay, - timer, - &mut event_logger, - &synced_date_time, - Some(watchdog), - true, - ); + match recording_type.as_mut().unwrap() { + RecordingType::LongRecording | RecordingType::TestRecording => { + info!("taken test recoridng clearing status"); + watchdog.feed(); + let _ = shared_i2c.tc2_agent_clear_and_set_flag( + &mut delay, + match recording_type.unwrap() { + RecordingType::LongRecording => { + tc2_agent_state::LONG_AUDIO_RECORDING + } + RecordingType::TestRecording => { + tc2_agent_state::TEST_AUDIO_RECORDING + } + _ => 0, + }, + if device_config.config().audio_mode != AudioMode::AudioOnly { + Some(tc2_agent_state::THERMAL_MODE) + } else { + None + }, + ); - restart(watchdog); - } else { - shared_i2c.clear_alarm(&mut delay); - reschedule = true; - clear_audio_alarm(&mut flash_storage); + let mut peripherals: Peripherals = unsafe { Peripherals::steal() }; - if thermal_requested_audio { - //if audio requested from thermal, the alarm will be re scheduled there - let _ = shared_i2c.tc2_agent_clear_and_set_flag( + offload_flash_storage_and_events( + &mut flash_storage, + &mut pi_spi, + &mut peripherals.RESETS, + &mut peripherals.DMA, + clock_freq, + &mut shared_i2c, &mut delay, - tc2_agent_state::TAKE_AUDIO, - tc2_agent_state::THERMAL_MODE, + timer, + &mut event_logger, + &synced_date_time, + Some(watchdog), + true, ); - info!("Audio taken in thermal window clearing flag"); + restart(watchdog); } + _ => { + shared_i2c.clear_alarm(&mut delay); + reschedule = true; + clear_audio_alarm(&mut flash_storage); + if thermal_requested_audio { + //if audio requested from thermal, the alarm will be re scheduled there + let _ = shared_i2c.tc2_agent_clear_and_set_flag( + &mut delay, + tc2_agent_state::TAKE_AUDIO, + Some(tc2_agent_state::THERMAL_MODE), + ); + info!("Audio taken in thermal window clearing flag"); + restart(watchdog); + } + } } } } @@ -696,7 +723,12 @@ pub fn schedule_audio_rec( error!("Failed to disable alarm"); return Err(()); } - let mut rng = RNG::::new(synced_date_time.date_time_utc.timestamp() as u64); + let seed = if device_config.config().audio_seed == 0 { + synced_date_time.date_time_utc.timestamp() as u64 + } else { + device_config.config().audio_seed as u64 + }; + let mut rng = RNG::::new(seed); let r = rng.generate(); let r_max: u16 = 65535u16; let short_chance: u16 = r_max / 4; diff --git a/src/core1_task.rs b/src/core1_task.rs index 0642dcb..b6b4549 100644 --- a/src/core1_task.rs +++ b/src/core1_task.rs @@ -410,17 +410,17 @@ pub fn core_1_task( &mut flash_storage, ); //changing from no audio to audio mode and then clicking test rec quickly - let test_rec = shared_i2c - .tc2_agent_requested_test_audio_rec(&mut delay) - .map_err(|e| error!("Error setting recording flag on attiny: {}", e)); - if let Ok(test_rec) = test_rec { - if test_rec { - sio.fifo.write_blocking(Core1Task::RequestReset.into()); - loop { - // Wait to be reset - nop(); + match shared_i2c.tc2_agent_requested_audio_recording(&mut delay) { + Ok(test_rec) => { + if test_rec.is_some() { + sio.fifo.write_blocking(Core1Task::RequestReset.into()); + loop { + // Wait to be reset + nop(); + } } } + Err(e) => error!("Error getting tc2 agent state: {}", e), } } diff --git a/src/device_config.rs b/src/device_config.rs index 37b3743..4792546 100644 --- a/src/device_config.rs +++ b/src/device_config.rs @@ -44,6 +44,7 @@ pub struct DeviceConfigInner { pub is_continuous_recorder: bool, pub use_low_power_mode: bool, pub audio_mode: AudioMode, + pub audio_seed: u32, } impl DeviceConfigInner { @@ -260,6 +261,7 @@ impl Default for DeviceConfig { is_continuous_recorder: false, use_low_power_mode: false, audio_mode: AudioMode::Disabled, + audio_seed: 0, }, motion_detection_mask: DetectionMask::new(None), cursor_position: 0, @@ -290,6 +292,7 @@ impl DeviceConfig { let audio_mode = AudioMode::try_from(cursor.read_u8()) .ok() .unwrap_or(AudioMode::Disabled); + let audio_seed = cursor.read_u32(); let latitude = cursor.read_f32(); let longitude = cursor.read_f32(); let has_location_timestamp = cursor.read_bool(); @@ -329,6 +332,7 @@ impl DeviceConfig { is_continuous_recorder, use_low_power_mode, audio_mode, + audio_seed, }, cursor.position(), )) diff --git a/src/main.rs b/src/main.rs index 89fe0ca..9c0624b 100644 --- a/src/main.rs +++ b/src/main.rs @@ -219,7 +219,8 @@ fn main() -> ! { let in_window = config.time_is_in_recording_window(&date_time, &None); if in_window { is_audio = (state - & (tc2_agent_state::TAKE_AUDIO + & (tc2_agent_state::LONG_AUDIO_RECORDING + | tc2_agent_state::TAKE_AUDIO | tc2_agent_state::TEST_AUDIO_RECORDING)) > 0; if is_audio { diff --git a/src/pdmfilter.rs b/src/pdmfilter.rs index 4555027..a6b84aa 100644 --- a/src/pdmfilter.rs +++ b/src/pdmfilter.rs @@ -1,7 +1,7 @@ const PDM_DECIMATION: u8 = 64; const PI: f32 = 3.14159; const SINCN: u8 = 3; -const FILTER_GAIN: u8 = 64; +const FILTER_GAIN: u8 = 16; const MAX_VOLUME: u8 = 64; //this is ported from @@ -40,6 +40,8 @@ impl PDMFilter { if lp_hz != 0.0 { self.lp_alpha = (lp_hz * 256.0 / (lp_hz + self.fs as f32 / (2.0 * PI))) as u32; } + + //high pass filter does not seem to be doing anything if hp_hz != 0.0 { self.hp_alpha = (self.fs as f32 * 256.0 / (2.0 * PI * hp_hz + self.fs as f32)) as u32; } @@ -55,13 +57,16 @@ impl PDMFilter { PDM_DECIMATION as usize, sinc_out.as_mut(), ); + + // https://s3.amazonaws.com/embeddedrelated/user/114298/lti%20filters_3552.pdf + //adding 0x00008000 to sum, to undo filter noise bias let sum = convolve( &sinc_out, PDM_DECIMATION as usize * 2 - 1, &sinc, PDM_DECIMATION as usize, sinc2[1..].as_mut(), - ); + ) + 0x00008000; self.sub_const = sum >> 1; self.div_const = self.sub_const * MAX_VOLUME as u32 / 32768 / FILTER_GAIN as u32;