Working early late: 3Kb/s
This commit is contained in:
3
out.txt
3
out.txt
@ -1,5 +1,6 @@
|
||||
Skibidi Toilet[1],[2] est une web-série[3] machinima, créée par Alexey Gerasimov et diffusée sur sa chaîne YouTube DaFuq!?Boom! Produite à l'aide de Source Filmmaker, la série suit une guerre fictive entre des toilettes à tête humaine et des personnages humanoïdes dotés d'appareils électroniques à la place de la tête.
|
||||
!<21>hbidi Toilet[1],[2] est une web-série[3] machinima, créée par Alexey Gerasimov et diffusée sur sa chaîne YouTube DaFuq!?Boom! Produite à l'aide de Source Filmmaker, la série suit une guerre fictive entre des toilettes à tête humaine et des personnages humanoïdes dotés d'appareils électroniques à la place de la tête.
|
||||
|
||||
|
||||
Après la publication du premier court métrage en février 2023, Skibidi Toilet devient un mème Internet viral sur divers réseaux sociaux, en particulier au sein de la génération Alpha. Les critiques ont vu dans cette série la première incursion de la génération Alpha dans la culture Internet, en concurrence avec la génération Z, plus âgée.
|
||||
|
||||
Synopsis
|
||||
|
||||
1
s.txt
1
s.txt
@ -1,5 +1,6 @@
|
||||
Skibidi Toilet[1],[2] est une web-série[3] machinima, créée par Alexey Gerasimov et diffusée sur sa chaîne YouTube DaFuq!?Boom! Produite à l'aide de Source Filmmaker, la série suit une guerre fictive entre des toilettes à tête humaine et des personnages humanoïdes dotés d'appareils électroniques à la place de la tête.
|
||||
|
||||
|
||||
Après la publication du premier court métrage en février 2023, Skibidi Toilet devient un mème Internet viral sur divers réseaux sociaux, en particulier au sein de la génération Alpha. Les critiques ont vu dans cette série la première incursion de la génération Alpha dans la culture Internet, en concurrence avec la génération Z, plus âgée.
|
||||
|
||||
Synopsis
|
||||
|
||||
@ -4,23 +4,28 @@ use std::collections::VecDeque;
|
||||
|
||||
use crate::complex::Complex32;
|
||||
|
||||
|
||||
pub struct FIRFilter
|
||||
{
|
||||
pub struct FIRFilter {
|
||||
size: usize,
|
||||
normalization: f32,
|
||||
impulse_response: Box<[Complex32]>,
|
||||
taps: VecDeque<Complex32>,
|
||||
}
|
||||
|
||||
impl FIRFilter
|
||||
{
|
||||
impl FIRFilter {
|
||||
pub fn new(impulse_response: &[Complex32]) -> Self {
|
||||
let normalization = impulse_response.iter().copied().sum::<Complex32>().mag(); // DC normalization
|
||||
println!("normalization factor {}", normalization);
|
||||
FIRFilter {
|
||||
size: impulse_response.len(),
|
||||
impulse_response: impulse_response.iter().copied().collect(),
|
||||
//normalization: impulse_response.iter().map(|x| x.mag()).reduce(|acc, e| acc.max(e)).unwrap(),
|
||||
normalization: impulse_response.iter().copied().sum::<Complex32>().mag(), // DC normalization
|
||||
/*
|
||||
normalization: impulse_response
|
||||
.iter()
|
||||
.map(|x| x.mag())
|
||||
.reduce(|acc, e| acc.max(e))
|
||||
.unwrap(),
|
||||
*/
|
||||
normalization: normalization.max(0.001), // DC normalization
|
||||
// TODO: Maybe we'd want other types of normalization (per frequency)
|
||||
taps: VecDeque::from(vec![Complex32::zero(); impulse_response.len()]),
|
||||
}
|
||||
@ -29,11 +34,12 @@ impl FIRFilter
|
||||
pub fn next(&mut self, next: Complex32) -> Complex32 {
|
||||
let _ = self.taps.pop_front();
|
||||
|
||||
self.taps.push_back(next);
|
||||
self.taps
|
||||
.iter()
|
||||
.zip(self.impulse_response.iter())
|
||||
.map(|(tap, coef)| *tap * *coef)
|
||||
.sum::<Complex32>() / self.normalization
|
||||
self.taps.push_back(next);
|
||||
self.taps
|
||||
.iter()
|
||||
.zip(self.impulse_response.iter())
|
||||
.map(|(tap, coef)| *tap * *coef)
|
||||
.sum::<Complex32>()
|
||||
/ self.normalization
|
||||
}
|
||||
}
|
||||
|
||||
276
src/main.rs
276
src/main.rs
@ -23,8 +23,8 @@ use complex::Complex32;
|
||||
use fft::DFTAlgorithm;
|
||||
use nco::Nco;
|
||||
|
||||
use eframe::egui::{self, Color32, debug_text::print};
|
||||
use egui_plot::{self, Bar, BarChart, Legend, Line, LineStyle, Plot, PlotPoints};
|
||||
use eframe::egui::{self, Color32, Context, debug_text::print};
|
||||
use egui_plot::{self, Bar, BarChart, Legend, Line, LineStyle, Plot, PlotPoints, VLine};
|
||||
use plotters::style::Color;
|
||||
|
||||
use crate::{
|
||||
@ -49,9 +49,15 @@ where
|
||||
fn main() {
|
||||
modulate();
|
||||
println!("Demodulating");
|
||||
//demodulate();
|
||||
demodulate();
|
||||
|
||||
return;
|
||||
let native_options = eframe::NativeOptions::default();
|
||||
let _ = eframe::run_native("Egui", native_options, Box::new(|cc| Ok(Box::new(EguiApp::new(cc)))));
|
||||
let _ = eframe::run_native(
|
||||
"Egui",
|
||||
native_options,
|
||||
Box::new(|cc| Ok(Box::new(EguiApp::new(cc)))),
|
||||
);
|
||||
}
|
||||
|
||||
#[derive(Default)]
|
||||
@ -60,15 +66,19 @@ struct EguiApp {
|
||||
|
||||
iq: Vec<Complex32>,
|
||||
dem: Vec<f32>,
|
||||
vlines: Vec<f32>,
|
||||
elg_sampling: Vec<f32>,
|
||||
eye_diagram: Vec<Vec<f32>>,
|
||||
}
|
||||
const BAUD_RATE: u32 = 1000;
|
||||
const BAUD_RATE: u32 = 3200;
|
||||
|
||||
impl EguiApp {
|
||||
fn new(cc: &eframe::CreationContext<'_>) -> Self {
|
||||
let mut reader = hound::WavReader::open("audio/modulated.wav").unwrap();
|
||||
let samples = reader.samples::<i16>();
|
||||
let sample_count = 10_000;
|
||||
let input_test = samples
|
||||
.take(10_000)
|
||||
.take(sample_count)
|
||||
.map(|x| x.unwrap() as f32 / i16::MAX as f32)
|
||||
.collect::<Vec<_>>();
|
||||
|
||||
@ -79,6 +89,11 @@ impl EguiApp {
|
||||
// Data parameters
|
||||
let sample_rate = 48000;
|
||||
let baud_rate = BAUD_RATE;
|
||||
let sample_per_symbol = sample_rate / baud_rate;
|
||||
let vlines = (0..sample_count)
|
||||
.filter(|i| i % sample_per_symbol as usize == 0)
|
||||
.map(|x| x as f32 / sample_count as f32)
|
||||
.collect();
|
||||
|
||||
let mut iq_sampler = IQSampler::new(hz_to_rad_per_sample(frequency, sample_rate as f32));
|
||||
let iq = input_test
|
||||
@ -86,29 +101,82 @@ impl EguiApp {
|
||||
.map(|x| iq_sampler.sample(*x))
|
||||
.collect::<Vec<_>>();
|
||||
|
||||
let mut dem = BFSKDem::new(
|
||||
sample_rate / baud_rate,
|
||||
hz_to_rad_per_sample(deviation, sample_rate as f32),
|
||||
);
|
||||
let mut demodulated = vec![];
|
||||
let mut nco_pos = Nco::new(hz_to_rad_per_sample(deviation, sample_rate as f32));
|
||||
let mut nco_neg = Nco::new(hz_to_rad_per_sample(-deviation, sample_rate as f32));
|
||||
let corellator_pos = (0..(sample_per_symbol * 1))
|
||||
.map(|_| {
|
||||
nco_pos.step();
|
||||
nco_pos.cexp()
|
||||
})
|
||||
.collect::<Vec<_>>();
|
||||
let corellator_neg = (0..(sample_per_symbol * 1))
|
||||
.map(|_| {
|
||||
nco_neg.step();
|
||||
nco_neg.cexp()
|
||||
})
|
||||
.collect::<Vec<_>>();
|
||||
|
||||
for x in iq.chunks((sample_rate / baud_rate) as usize) {
|
||||
let samples = x
|
||||
.iter()
|
||||
.copied()
|
||||
.chain(std::iter::repeat(Complex32::zero()))
|
||||
.take((sample_rate / baud_rate) as usize)
|
||||
.collect::<Vec<_>>();
|
||||
demodulated.push(dem.demod(&samples));
|
||||
let mut matched_filter_pos = FIRFilter::new(&corellator_pos);
|
||||
let mut matched_filter_neg = FIRFilter::new(&corellator_neg);
|
||||
|
||||
let mut dem = vec![];
|
||||
|
||||
let mut loop_filter = FIRFilter::new(&[Complex32::new(1., 0.); 1]);
|
||||
for x in &iq {
|
||||
let pos = matched_filter_pos.next(x.clone());
|
||||
let neg = matched_filter_neg.next(*x);
|
||||
dem.push(
|
||||
loop_filter
|
||||
.next(Complex32::new(pos.mag() - neg.mag(), 0.))
|
||||
.re,
|
||||
);
|
||||
}
|
||||
|
||||
// Symbol recovery
|
||||
|
||||
let mut sample_ids = vec![];
|
||||
let delta = 0.5;
|
||||
let alpha = 0.01;
|
||||
let mut current_sps = sample_per_symbol as f32;
|
||||
let mut current_position = current_sps / 2.;
|
||||
|
||||
let mut eye_diagram = vec![];
|
||||
while current_position < dem.len() as f32 {
|
||||
// Sample before after
|
||||
let early_id = (current_position - (delta * current_sps)).max(0.).floor() as u32;
|
||||
let late_id = (current_position + (delta * current_sps)).max(0.).floor() as u32;
|
||||
if late_id as usize >= dem.len() {
|
||||
break;
|
||||
}
|
||||
let early = dem[early_id as usize];
|
||||
let late = dem[late_id as usize];
|
||||
let error = early * early - late * late;
|
||||
current_sps -= alpha * error;
|
||||
|
||||
sample_ids.push(current_position);
|
||||
|
||||
let eye = ((current_position - current_sps).max(0.) as usize
|
||||
..(current_position + current_sps).min(sample_count as f32) as usize)
|
||||
.map(|i| dem[i])
|
||||
.collect();
|
||||
|
||||
eye_diagram.push(eye);
|
||||
|
||||
current_position += current_sps;
|
||||
}
|
||||
|
||||
let elg_sampling = sample_ids
|
||||
.iter()
|
||||
.map(|x| *x / sample_count as f32)
|
||||
.collect();
|
||||
|
||||
Self {
|
||||
samples: input_test.clone(),
|
||||
iq,
|
||||
dem: demodulated
|
||||
.iter()
|
||||
.map(|b| if *b { 1. } else { 0. })
|
||||
.collect(),
|
||||
dem,
|
||||
vlines,
|
||||
elg_sampling,
|
||||
eye_diagram,
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -120,9 +188,41 @@ impl eframe::App for EguiApp {
|
||||
Plot::new("Fourrier transform")
|
||||
.legend(Legend::default())
|
||||
.show(ui, |plot_ui| {
|
||||
for (i, eye) in self.eye_diagram.iter().enumerate().skip(1) {
|
||||
plot_ui.line(
|
||||
Line::new(
|
||||
"eye",
|
||||
eye.iter()
|
||||
.enumerate()
|
||||
.map(|(i, x)| [i as f64 / eye.len() as f64, *x as f64])
|
||||
.collect::<Vec<_>>(),
|
||||
)
|
||||
.width(1.)
|
||||
.color(Color32::RED),
|
||||
);
|
||||
}
|
||||
|
||||
self.vlines.iter().for_each(|l| {
|
||||
plot_ui.vline(
|
||||
VLine::new("Boundaries", *l as f64)
|
||||
.color(Color32::LIGHT_BLUE)
|
||||
.width(0.5)
|
||||
.style(LineStyle::dashed_dense()),
|
||||
);
|
||||
});
|
||||
|
||||
self.elg_sampling.iter().for_each(|l| {
|
||||
plot_ui.vline(
|
||||
VLine::new("ELG", *l as f64)
|
||||
.color(Color32::RED)
|
||||
.width(0.5)
|
||||
.style(LineStyle::dotted_dense()),
|
||||
);
|
||||
});
|
||||
|
||||
plot_ui.line(
|
||||
Line::new(
|
||||
"Transfer function",
|
||||
"Passband",
|
||||
self.samples
|
||||
.iter()
|
||||
.enumerate()
|
||||
@ -134,44 +234,11 @@ impl eframe::App for EguiApp {
|
||||
|
||||
plot_ui.line(
|
||||
Line::new(
|
||||
"RE",
|
||||
self.iq
|
||||
"Demodulated",
|
||||
self.dem
|
||||
.iter()
|
||||
.enumerate()
|
||||
.map(|(i, x)| [i as f64 / self.iq.len() as f64, x.re as f64])
|
||||
.collect::<Vec<_>>(),
|
||||
)
|
||||
.width(2.),
|
||||
);
|
||||
|
||||
plot_ui.line(
|
||||
Line::new(
|
||||
"IM",
|
||||
self.iq
|
||||
.iter()
|
||||
.enumerate()
|
||||
.map(|(i, x)| [i as f64 / self.iq.len() as f64, x.im as f64])
|
||||
.collect::<Vec<_>>(),
|
||||
)
|
||||
.width(2.),
|
||||
);
|
||||
|
||||
plot_ui.line(
|
||||
Line::new(
|
||||
"Bits",
|
||||
self.iq
|
||||
.iter()
|
||||
.enumerate()
|
||||
.map(|(i, x)| {
|
||||
[
|
||||
i as f64 / self.iq.len() as f64,
|
||||
self.dem[(self.dem.len() as f32 * i as f32
|
||||
/ self.iq.len() as f32)
|
||||
.floor()
|
||||
as usize]
|
||||
as f64,
|
||||
]
|
||||
})
|
||||
.map(|(i, x)| [i as f64 / self.iq.len() as f64, *x as f64])
|
||||
.collect::<Vec<_>>(),
|
||||
)
|
||||
.width(2.),
|
||||
@ -189,33 +256,88 @@ fn demodulate() {
|
||||
let frequency = 1700.;
|
||||
let deviation = 500.;
|
||||
|
||||
// Data parameter
|
||||
// Data parameters
|
||||
let sample_rate = 48000;
|
||||
let baud_rate = BAUD_RATE;
|
||||
let sample_per_symbol = sample_rate / baud_rate;
|
||||
|
||||
let mut iq_sampler = IQSampler::new(hz_to_rad_per_sample(frequency, sample_rate as f32));
|
||||
let iq_samples = samples
|
||||
let iq = samples
|
||||
.map(|x| iq_sampler.sample(x.unwrap() as f32 / i16::MAX as f32))
|
||||
.collect::<Vec<_>>();
|
||||
|
||||
let mut dem = BFSKDem::new(
|
||||
sample_rate / baud_rate,
|
||||
hz_to_rad_per_sample(deviation, sample_rate as f32),
|
||||
);
|
||||
let mut bits = vec![];
|
||||
let mut nco_pos = Nco::new(hz_to_rad_per_sample(deviation, sample_rate as f32));
|
||||
let mut nco_neg = Nco::new(hz_to_rad_per_sample(-deviation, sample_rate as f32));
|
||||
let corellator_pos = (0..(sample_per_symbol * 1))
|
||||
.map(|_| {
|
||||
nco_pos.step();
|
||||
nco_pos.cexp()
|
||||
})
|
||||
.collect::<Vec<_>>();
|
||||
let corellator_neg = (0..(sample_per_symbol * 1))
|
||||
.map(|_| {
|
||||
nco_neg.step();
|
||||
nco_neg.cexp()
|
||||
})
|
||||
.collect::<Vec<_>>();
|
||||
|
||||
for x in iq_samples.chunks((sample_rate / baud_rate) as usize) {
|
||||
// Zero pad
|
||||
//let zero_padded = x.iter().copied().chain(std::iter::repeat(Complex32::zero())).take(sample_rate as usize / baud_rate as usize).collect::<Vec<_>>();
|
||||
//bits.push(dem.demod(&zero_padded));
|
||||
bits.push(dem.demod(x));
|
||||
let mut matched_filter_pos = FIRFilter::new(&corellator_pos);
|
||||
let mut matched_filter_neg = FIRFilter::new(&corellator_neg);
|
||||
|
||||
let mut dem = vec![];
|
||||
|
||||
for x in &iq {
|
||||
let pos = matched_filter_pos.next(x.clone());
|
||||
let neg = matched_filter_neg.next(*x);
|
||||
dem.push(pos.mag() - neg.mag());
|
||||
}
|
||||
|
||||
assert!(bits.len() % 8 == 0);
|
||||
// Symbol recovery
|
||||
|
||||
let mut bits = vec![];
|
||||
let delta = 0.5;
|
||||
let alpha = 0.01;
|
||||
let mut current_sps = sample_per_symbol as f32;
|
||||
let mut current_position = current_sps / 2.;
|
||||
|
||||
while current_position < dem.len() as f32 {
|
||||
// Sample before after
|
||||
let early_id = (current_position - (delta * current_sps)).max(0.).floor() as u32;
|
||||
let late_id = (current_position + (delta * current_sps)).max(0.).floor() as u32;
|
||||
if late_id as usize >= dem.len() {
|
||||
break;
|
||||
}
|
||||
let early = dem[early_id as usize];
|
||||
let late = dem[late_id as usize];
|
||||
let error = early * early - late * late;
|
||||
current_sps -= alpha * error;
|
||||
|
||||
bits.push(dem[current_position.floor() as usize] > 0.);
|
||||
|
||||
current_position += current_sps;
|
||||
}
|
||||
|
||||
//assert!(bits.len() % 8 == 0);
|
||||
|
||||
let mut out_file = File::create("out.txt").unwrap();
|
||||
let mut strip = 0;
|
||||
|
||||
let bit_slice = bits.as_slice();
|
||||
for i in 0..100 {
|
||||
let byte = bits_to_byte(&bit_slice[(i as usize)..(i as usize + 8)]);
|
||||
if byte == 0b01010111u8 {
|
||||
strip = i + 8;
|
||||
}
|
||||
}
|
||||
|
||||
for i in 0..strip {
|
||||
bits.remove(i as usize);
|
||||
}
|
||||
for x in bits.chunks(8) {
|
||||
out_file.write_all(&[!bits_to_byte(x)]).unwrap();
|
||||
if x.len() != 8 {
|
||||
break;
|
||||
}
|
||||
out_file.write_all(&[bits_to_byte(x)]).unwrap();
|
||||
}
|
||||
}
|
||||
|
||||
@ -230,7 +352,11 @@ fn modulate() {
|
||||
|
||||
// File to modulate
|
||||
let f = File::open("s.txt").unwrap();
|
||||
let mut bitstream = f.bytes().flat_map(|b| byte_to_bits(b.unwrap()));
|
||||
let mut bitstream = std::iter::repeat_n(0b01010101u8, 1)
|
||||
.chain(std::iter::repeat_n(0b01010111u8, 1))
|
||||
.chain(f.bytes().map(|x| x.unwrap()))
|
||||
.chain(std::iter::repeat_n(0u8, 1))
|
||||
.flat_map(byte_to_bits);
|
||||
|
||||
let mut modulator = BFSKMod::new(
|
||||
sample_rate / baud_rate,
|
||||
|
||||
Reference in New Issue
Block a user