port more from osmocom
This commit is contained in:
@@ -1,17 +1,19 @@
|
|||||||
|
const FSK_MAX_BITS: i32 = 1032;
|
||||||
|
|
||||||
enum DSPMode {
|
enum DSPMode {
|
||||||
OFF, // Channel not active
|
OFF, // Channel not active
|
||||||
AUDIO_RX_AUDIO_TX, // stream audio
|
AudioRxAudioTx, // stream audio
|
||||||
AUDIO_RX_FRAME_TX, // stream audio and send frames
|
AudioRxFrameTx, // stream audio and send frames
|
||||||
AUDIO_RX_SILENCE_TX, // stream audio and send silence
|
AudioRxSilenceTx, // stream audio and send silence
|
||||||
FRAME_RX_FRAME_TX, // send and receive frames
|
FrameRxFrameTx, // send and receive frames
|
||||||
}
|
}
|
||||||
|
|
||||||
enum AMPSChannelType {
|
enum AMPSChannelType {
|
||||||
CC, // control channel
|
CC, // control channel
|
||||||
PC, // paging channel
|
PC, // paging channel
|
||||||
CC_PC, // combined control+paging
|
CcPc, // combined control+paging
|
||||||
VC, // voice channel
|
VC, // voice channel
|
||||||
CC_PC_VC, // combined control+paging+voice
|
CcPcVc, // combined control+paging+voice
|
||||||
}
|
}
|
||||||
|
|
||||||
enum AMPSState {
|
enum AMPSState {
|
||||||
@@ -20,7 +22,7 @@ enum AMPSState {
|
|||||||
BUSY, // channel busy (call in progress)
|
BUSY, // channel busy (call in progress)
|
||||||
}
|
}
|
||||||
|
|
||||||
enum FSK_RX_Sync {
|
enum FSKRxSync {
|
||||||
NONE, // not in sync, waiting for valid dotting sequence
|
NONE, // not in sync, waiting for valid dotting sequence
|
||||||
DOTTING, // received a valid dotting sequence, check for sync sequence
|
DOTTING, // received a valid dotting sequence, check for sync sequence
|
||||||
POSITIVE, // valid sync, read all bits from the frame
|
POSITIVE, // valid sync, read all bits from the frame
|
||||||
|
|||||||
13
cellular/src/display.rs
Normal file
13
cellular/src/display.rs
Normal file
@@ -0,0 +1,13 @@
|
|||||||
|
// Stub for display
|
||||||
|
#[derive(Clone)]
|
||||||
|
pub struct DispWav;
|
||||||
|
#[derive(Clone)]
|
||||||
|
pub struct DispMeas;
|
||||||
|
|
||||||
|
impl DispWav {
|
||||||
|
pub fn new() -> Self { DispWav }
|
||||||
|
}
|
||||||
|
|
||||||
|
impl DispMeas {
|
||||||
|
pub fn new() -> Self { DispMeas }
|
||||||
|
}
|
||||||
57
cellular/src/emphasis.rs
Normal file
57
cellular/src/emphasis.rs
Normal file
@@ -0,0 +1,57 @@
|
|||||||
|
use crate::iir_filter::IIRFilter;
|
||||||
|
|
||||||
|
type Sample = f64;
|
||||||
|
|
||||||
|
#[derive(Clone)]
|
||||||
|
pub struct Emphasis {
|
||||||
|
pub pre_filter: IIRFilter,
|
||||||
|
pub de_filter: IIRFilter,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Emphasis {
|
||||||
|
pub fn new() -> Self {
|
||||||
|
Emphasis {
|
||||||
|
pre_filter: IIRFilter {
|
||||||
|
iter: 0,
|
||||||
|
a0: 0.0,
|
||||||
|
a1: 0.0,
|
||||||
|
a2: 0.0,
|
||||||
|
b1: 0.0,
|
||||||
|
b2: 0.0,
|
||||||
|
z1: Vec::new(),
|
||||||
|
z2: Vec::new(),
|
||||||
|
},
|
||||||
|
de_filter: IIRFilter {
|
||||||
|
iter: 0,
|
||||||
|
a0: 0.0,
|
||||||
|
a1: 0.0,
|
||||||
|
a2: 0.0,
|
||||||
|
b1: 0.0,
|
||||||
|
b2: 0.0,
|
||||||
|
z1: Vec::new(),
|
||||||
|
z2: Vec::new(),
|
||||||
|
},
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn init(&mut self, samplerate: i32, pre_emphasis: i32, de_emphasis: i32) {
|
||||||
|
if pre_emphasis != 0 {
|
||||||
|
self.pre_filter.highpass_init(300.0, samplerate, 1); // Boost highs
|
||||||
|
}
|
||||||
|
if de_emphasis != 0 {
|
||||||
|
self.de_filter.lowpass_init(3000.0, samplerate, 1); // Cut highs
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn process_pre(&mut self, samples: &mut [Sample]) {
|
||||||
|
if self.pre_filter.iter > 0 {
|
||||||
|
self.pre_filter.process(samples);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn process_de(&mut self, samples: &mut [Sample]) {
|
||||||
|
if self.de_filter.iter > 0 {
|
||||||
|
self.de_filter.process(samples);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -1,36 +1,130 @@
|
|||||||
struct IIRFilter {
|
type Sample = f64;
|
||||||
iter: i32,
|
|
||||||
a0: f64,
|
#[derive(Clone)]
|
||||||
a1: f64,
|
pub struct IIRFilter {
|
||||||
a2: f64,
|
pub iter: i32,
|
||||||
b1: f64,
|
pub a0: f64,
|
||||||
b2: f64,
|
pub a1: f64,
|
||||||
z1: f64,
|
pub a2: f64,
|
||||||
z2: f64,
|
pub b1: f64,
|
||||||
|
pub b2: f64,
|
||||||
|
pub z1: Vec<f64>, // Changed to Vec for iterations
|
||||||
|
pub z2: Vec<f64>, // Changed to Vec for iterations
|
||||||
}
|
}
|
||||||
|
|
||||||
impl IIRFilter {
|
impl IIRFilter {
|
||||||
pub fn lowpass_init(&mut self, frequency: f64, samplerate: i32, iterations: i32) {
|
pub fn lowpass_init(&mut self, frequency: f64, samplerate: i32, iterations: i32) {
|
||||||
|
if iterations > 64 {
|
||||||
|
panic!("Too many iterations, please fix!");
|
||||||
|
}
|
||||||
|
let sr = samplerate as f64;
|
||||||
|
let fc = frequency / sr; // Normalized frequency
|
||||||
|
let q = (0.5f64).sqrt().powf(1.0 / iterations as f64); // Q factor for iterations
|
||||||
|
let k = (std::f64::consts::PI * fc).tan();
|
||||||
|
let norm = 1.0 / (1.0 + k / q + k * k);
|
||||||
|
self.a0 = k * k * norm;
|
||||||
|
self.a1 = 2.0 * self.a0;
|
||||||
|
self.a2 = self.a0;
|
||||||
|
self.b1 = 2.0 * (k * k - 1.0) * norm;
|
||||||
|
self.b2 = (1.0 - k / q + k * k) * norm;
|
||||||
|
self.iter = iterations;
|
||||||
|
self.z1 = vec![0.0; iterations as usize];
|
||||||
|
self.z2 = vec![0.0; iterations as usize];
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn highpass_init(&mut self, frequency: f64, samplerate: i32, iterations: i32) {
|
pub fn highpass_init(&mut self, frequency: f64, samplerate: i32, iterations: i32) {
|
||||||
|
if iterations > 64 {
|
||||||
|
panic!("Too many iterations, please fix!");
|
||||||
|
}
|
||||||
|
let sr = samplerate as f64;
|
||||||
|
let fc = frequency / sr;
|
||||||
|
let q = (0.5f64).sqrt().powf(1.0 / iterations as f64);
|
||||||
|
let k = (std::f64::consts::PI * fc).tan();
|
||||||
|
let norm = 1.0 / (1.0 + k / q + k * k);
|
||||||
|
self.a0 = 1.0 * norm;
|
||||||
|
self.a1 = -2.0 * self.a0;
|
||||||
|
self.a2 = self.a0;
|
||||||
|
self.b1 = 2.0 * (k * k - 1.0) * norm;
|
||||||
|
self.b2 = (1.0 - k / q + k * k) * norm;
|
||||||
|
self.iter = iterations;
|
||||||
|
self.z1 = vec![0.0; iterations as usize];
|
||||||
|
self.z2 = vec![0.0; iterations as usize];
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn bandpass_init(&mut self, frequency: f64, samplerate: i32, iterations: i32) {
|
pub fn bandpass_init(&mut self, frequency: f64, samplerate: i32, iterations: i32) {
|
||||||
|
if iterations > 64 {
|
||||||
|
panic!("Too many iterations, please fix!");
|
||||||
|
}
|
||||||
|
let sr = samplerate as f64;
|
||||||
|
let fc = frequency / sr;
|
||||||
|
let q = (0.5f64).sqrt().powf(1.0 / iterations as f64);
|
||||||
|
let k = (std::f64::consts::PI * fc).tan();
|
||||||
|
let norm = 1.0 / (1.0 + k / q + k * k);
|
||||||
|
self.a0 = k / q * norm;
|
||||||
|
self.a1 = 0.0;
|
||||||
|
self.a2 = -self.a0;
|
||||||
|
self.b1 = 2.0 * (k * k - 1.0) * norm;
|
||||||
|
self.b2 = (1.0 - k / q + k * k) * norm;
|
||||||
|
self.iter = iterations;
|
||||||
|
self.z1 = vec![0.0; iterations as usize];
|
||||||
|
self.z2 = vec![0.0; iterations as usize];
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn notch_init(&mut self, frequency: f64, samplerate: i32, iterations: i32, Q: f64) {
|
pub fn notch_init(&mut self, frequency: f64, samplerate: i32, iterations: i32, q: f64) {
|
||||||
|
if iterations > 64 {
|
||||||
|
panic!("Too many iterations, please fix!");
|
||||||
|
}
|
||||||
|
let sr = samplerate as f64;
|
||||||
|
let fc = frequency / sr;
|
||||||
|
let k = (std::f64::consts::PI * fc).tan();
|
||||||
|
let norm = 1.0 / (1.0 + k / q + k * k);
|
||||||
|
self.a0 = (1.0 + k * k) * norm;
|
||||||
|
self.a1 = 2.0 * (k * k - 1.0) * norm;
|
||||||
|
self.a2 = self.a0;
|
||||||
|
self.b1 = self.a1;
|
||||||
|
self.b2 = (1.0 - k / q + k * k) * norm;
|
||||||
|
self.iter = iterations;
|
||||||
|
self.z1 = vec![0.0; iterations as usize];
|
||||||
|
self.z2 = vec![0.0; iterations as usize];
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn process(&mut self, samples: Sample, length: i32) {
|
pub fn process(&mut self, samples: &mut [Sample]) {
|
||||||
|
let a0 = self.a0;
|
||||||
|
let a1 = self.a1;
|
||||||
|
let a2 = self.a2;
|
||||||
|
let b1 = self.b1;
|
||||||
|
let b2 = self.b2;
|
||||||
|
let iterations = self.iter as usize;
|
||||||
|
|
||||||
|
for sample in samples.iter_mut() {
|
||||||
|
let mut input = *sample + 0.000000001; // As in C
|
||||||
|
for j in 0..iterations {
|
||||||
|
let out = input * a0 + self.z1[j];
|
||||||
|
self.z1[j] = input * a1 + self.z2[j] - b1 * out;
|
||||||
|
self.z2[j] = input * a2 - b2 * out;
|
||||||
|
input = out;
|
||||||
|
}
|
||||||
|
*sample = input;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn process_baseband(&mut self, filter: IIRFilter, baseband: f64, length: i32) {
|
pub fn process_baseband(&mut self, baseband: &mut [f32], length: i32) {
|
||||||
|
let a0 = self.a0;
|
||||||
|
let a1 = self.a1;
|
||||||
|
let a2 = self.a2;
|
||||||
|
let b1 = self.b1;
|
||||||
|
let b2 = self.b2;
|
||||||
|
let iterations = self.iter as usize;
|
||||||
|
|
||||||
|
for i in 0..length as usize {
|
||||||
|
let mut input = baseband[i * 2] as f64 + 0.000000001; // Process real part
|
||||||
|
for j in 0..iterations {
|
||||||
|
let out = input * a0 + self.z1[j];
|
||||||
|
self.z1[j] = input * a1 + self.z2[j] - b1 * out;
|
||||||
|
self.z2[j] = input * a2 - b2 * out;
|
||||||
|
input = out;
|
||||||
|
}
|
||||||
|
baseband[i * 2] = input as f32;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
41
cellular/src/jitter.rs
Normal file
41
cellular/src/jitter.rs
Normal file
@@ -0,0 +1,41 @@
|
|||||||
|
type Sample = f64;
|
||||||
|
|
||||||
|
#[derive(Clone)]
|
||||||
|
pub struct Jitter {
|
||||||
|
pub buffer: Vec<Sample>,
|
||||||
|
pub read_pos: usize,
|
||||||
|
pub write_pos: usize,
|
||||||
|
pub size: usize,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Jitter {
|
||||||
|
pub fn new(size: usize) -> Self {
|
||||||
|
Jitter {
|
||||||
|
buffer: vec![0.0; size],
|
||||||
|
read_pos: 0,
|
||||||
|
write_pos: 0,
|
||||||
|
size,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn write(&mut self, samples: &[Sample]) {
|
||||||
|
for &sample in samples {
|
||||||
|
self.buffer[self.write_pos] = sample;
|
||||||
|
self.write_pos = (self.write_pos + 1) % self.size;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn read(&mut self, output: &mut [Sample]) -> usize {
|
||||||
|
let mut count = 0;
|
||||||
|
for i in 0..output.len() {
|
||||||
|
if self.read_pos != self.write_pos {
|
||||||
|
output[i] = self.buffer[self.read_pos];
|
||||||
|
self.read_pos = (self.read_pos + 1) % self.size;
|
||||||
|
count += 1;
|
||||||
|
} else {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
count
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -1,17 +1,35 @@
|
|||||||
|
use std::ffi::{c_void, CStr, c_char};
|
||||||
|
use crate::emphasis::Emphasis;
|
||||||
|
use crate::jitter::Jitter;
|
||||||
|
use crate::wave::{WaveRec, WavePlay};
|
||||||
|
use crate::display::{DispWav, DispMeas};
|
||||||
|
use crate::samplerate::SampleRate;
|
||||||
|
|
||||||
|
// Type aliases
|
||||||
|
type Sample = f64;
|
||||||
|
|
||||||
|
#[repr(C)]
|
||||||
|
#[derive(Clone)]
|
||||||
enum PagingSignal {
|
enum PagingSignal {
|
||||||
NONE = 0,
|
None = 0,
|
||||||
TONE,
|
Tone,
|
||||||
NOTONE,
|
NoTone,
|
||||||
POSITIVE,
|
Positive,
|
||||||
NEGATIVE,
|
Negative,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[derive(Clone)]
|
||||||
struct Sender {
|
struct Sender {
|
||||||
// System Info
|
next: *mut Sender,
|
||||||
channel: char,
|
slave: *mut Sender,
|
||||||
send_freq: f64,
|
master: *mut Sender,
|
||||||
recv_freq: f64,
|
|
||||||
|
// System info
|
||||||
|
kanal: String,
|
||||||
|
sendefrequenz: f64,
|
||||||
|
empfangsfrequenz: f64,
|
||||||
|
ruffrequenz: f64,
|
||||||
|
|
||||||
// FM/AM levels
|
// FM/AM levels
|
||||||
am: i32,
|
am: i32,
|
||||||
max_deviation: f64,
|
max_deviation: f64,
|
||||||
@@ -21,25 +39,168 @@ struct Sender {
|
|||||||
max_display: f64,
|
max_display: f64,
|
||||||
|
|
||||||
// Audio
|
// Audio
|
||||||
audio: void,
|
audio: *mut c_void,
|
||||||
device: char,
|
device: String,
|
||||||
audio_open: void,
|
audio_open: Option<fn(i32, *const std::ffi::c_char, *mut f64, *mut f64, *mut i32, i32, f64, i32, i32, f64, f64, f64, f64) -> *mut c_void>,
|
||||||
audio_start: i32,
|
audio_start: Option<fn(*mut c_void) -> i32>,
|
||||||
audio_close: void,
|
audio_close: Option<fn(*mut c_void)>,
|
||||||
audio_write: i32,
|
audio_write: Option<fn(*mut c_void, *mut *mut Sample, *mut *mut u8, i32, *mut PagingSignal, *mut i32, i32) -> i32>,
|
||||||
audio_get_tosend: i32,
|
audio_read: Option<fn(*mut c_void, *mut *mut Sample, i32, i32, *mut f64) -> i32>,
|
||||||
|
audio_get_tosend: Option<fn(*mut c_void, i32) -> i32>,
|
||||||
samplerate: i32,
|
samplerate: i32,
|
||||||
samplerate_state: SampleRate,
|
srstate: SampleRate,
|
||||||
rx_gain: f64,
|
rx_gain: f64,
|
||||||
tx_gain: f64,
|
tx_gain: f64,
|
||||||
pre_emphasis: i32,
|
pre_emphasis: i32,
|
||||||
de_emphasis: i32,
|
de_emphasis: i32,
|
||||||
epmphasis_state: EmphasisState,
|
estate: Emphasis,
|
||||||
|
|
||||||
// Loopback
|
// Loopback test
|
||||||
loopback: i32,
|
loopback: i32,
|
||||||
|
|
||||||
// Record & Playback
|
// Record and playback
|
||||||
|
write_rx_wave: Option<String>,
|
||||||
|
write_tx_wave: Option<String>,
|
||||||
|
read_rx_wave: Option<String>,
|
||||||
|
read_tx_wave: Option<String>,
|
||||||
|
wave_rx_rec: WaveRec,
|
||||||
|
wave_tx_rec: WaveRec,
|
||||||
|
wave_rx_play: WavePlay,
|
||||||
|
wave_tx_play: WavePlay,
|
||||||
|
|
||||||
}
|
// Audio buffer for audio to send to transmitter
|
||||||
|
dejitter: Jitter,
|
||||||
|
loop_dejitter: Jitter,
|
||||||
|
loop_sequence: u16,
|
||||||
|
loop_timestamp: u32,
|
||||||
|
|
||||||
|
// Audio buffer for audio to send to caller (20ms = 160 samples @ 8000Hz)
|
||||||
|
rxbuf: [Sample; 160],
|
||||||
|
rxbuf_pos: i32,
|
||||||
|
|
||||||
|
// Paging tone
|
||||||
|
paging_signal: PagingSignal,
|
||||||
|
paging_on: i32,
|
||||||
|
|
||||||
|
// Display wave
|
||||||
|
dispwav: DispWav,
|
||||||
|
|
||||||
|
// Display measurements
|
||||||
|
dispmeas: DispMeas,
|
||||||
|
}
|
||||||
|
|
||||||
|
// Global variables
|
||||||
|
pub static mut SENDER_HEAD: *mut Sender = std::ptr::null_mut();
|
||||||
|
pub static mut CANT_RECOVER: i32 = 0;
|
||||||
|
pub static mut CHECK_CHANNEL: i32 = 0;
|
||||||
|
|
||||||
|
// Function stubs (to be implemented)
|
||||||
|
pub fn sender_create(
|
||||||
|
sender: *mut Sender,
|
||||||
|
kanal: *const c_char,
|
||||||
|
sendefrequenz: f64,
|
||||||
|
empfangsfrequenz: f64,
|
||||||
|
device: *const c_char,
|
||||||
|
use_sdr: i32,
|
||||||
|
samplerate: i32,
|
||||||
|
rx_gain: f64,
|
||||||
|
tx_gain: f64,
|
||||||
|
pre_emphasis: i32,
|
||||||
|
de_emphasis: i32,
|
||||||
|
write_rx_wave: *const c_char,
|
||||||
|
write_tx_wave: *const c_char,
|
||||||
|
read_rx_wave: *const c_char,
|
||||||
|
read_tx_wave: *const c_char,
|
||||||
|
loopback: i32,
|
||||||
|
paging_signal: PagingSignal,
|
||||||
|
) -> i32 {
|
||||||
|
unsafe {
|
||||||
|
if sender.is_null() {
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
let s = &mut *sender;
|
||||||
|
s.kanal = CStr::from_ptr(kanal).to_string_lossy().into_owned();
|
||||||
|
s.sendefrequenz = sendefrequenz;
|
||||||
|
s.empfangsfrequenz = empfangsfrequenz;
|
||||||
|
s.device = CStr::from_ptr(device).to_string_lossy().into_owned();
|
||||||
|
s.samplerate = samplerate;
|
||||||
|
s.rx_gain = rx_gain;
|
||||||
|
s.tx_gain = tx_gain;
|
||||||
|
s.pre_emphasis = pre_emphasis;
|
||||||
|
s.de_emphasis = de_emphasis;
|
||||||
|
s.write_rx_wave = if !write_rx_wave.is_null() {
|
||||||
|
Some(CStr::from_ptr(write_rx_wave).to_string_lossy().into_owned())
|
||||||
|
} else {
|
||||||
|
None
|
||||||
|
};
|
||||||
|
s.write_tx_wave = if !write_tx_wave.is_null() {
|
||||||
|
Some(CStr::from_ptr(write_tx_wave).to_string_lossy().into_owned())
|
||||||
|
} else {
|
||||||
|
None
|
||||||
|
};
|
||||||
|
s.read_rx_wave = if !read_rx_wave.is_null() {
|
||||||
|
Some(CStr::from_ptr(read_rx_wave).to_string_lossy().into_owned())
|
||||||
|
} else {
|
||||||
|
None
|
||||||
|
};
|
||||||
|
s.read_tx_wave = if !read_tx_wave.is_null() {
|
||||||
|
Some(CStr::from_ptr(read_tx_wave).to_string_lossy().into_owned())
|
||||||
|
} else {
|
||||||
|
None
|
||||||
|
};
|
||||||
|
s.loopback = loopback;
|
||||||
|
s.paging_signal = paging_signal;
|
||||||
|
// Initialize dependencies
|
||||||
|
s.estate = Emphasis::new();
|
||||||
|
s.dejitter = Jitter::new(1600); // 10 * 160
|
||||||
|
s.loop_dejitter = Jitter::new(1600);
|
||||||
|
s.wave_rx_rec = WaveRec::new();
|
||||||
|
s.wave_tx_rec = WaveRec::new();
|
||||||
|
s.wave_rx_play = WavePlay::new();
|
||||||
|
s.wave_tx_play = WavePlay::new();
|
||||||
|
s.dispwav = DispWav::new();
|
||||||
|
s.dispmeas = DispMeas::new();
|
||||||
|
// TODO: Initialize srstate
|
||||||
|
// s.srstate = SampleRate::new(...);
|
||||||
|
// TODO: Initialize audio_open etc.
|
||||||
|
s.audio_open = None; // Stub
|
||||||
|
s.audio_start = None;
|
||||||
|
s.audio_close = None;
|
||||||
|
s.audio_write = None;
|
||||||
|
s.audio_read = None;
|
||||||
|
s.audio_get_tosend = None;
|
||||||
|
0
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn sender_destroy(sender: *mut Sender) {
|
||||||
|
if !sender.is_null() {
|
||||||
|
// TODO: Clean up resources
|
||||||
|
// For now, just deallocate if using Box or something, but since *mut, assume caller handles
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Stub implementations for other functions
|
||||||
|
pub fn sender_set_fm(_sender: *mut Sender, _max_deviation: f64, _max_modulation: f64, _speech_deviation: f64, _max_display: f64) {}
|
||||||
|
pub fn sender_set_am(_sender: *mut Sender, _max_modulation: f64, _speech_deviation: f64, _max_display: f64, _modulation_index: f64) {}
|
||||||
|
pub fn sender_open_audio(_buffer_size: i32, _interval: f64) -> i32 { 0 }
|
||||||
|
pub fn sender_start_audio() -> i32 { 0 }
|
||||||
|
pub fn process_sender_audio(_sender: *mut Sender, _quit: *mut i32, _samples: *mut *mut Sample, _power: *mut *mut u8, _buffer_size: i32) {}
|
||||||
|
pub fn sender_send(_sender: *mut Sender, _samples: *mut Sample, _power: *mut u8, _count: i32) {}
|
||||||
|
pub fn sender_receive(_sender: *mut Sender, _samples: *mut Sample, _count: i32, _rf_level_db: f64) {}
|
||||||
|
pub fn sender_paging(_sender: *mut Sender, _on: i32) {}
|
||||||
|
|
||||||
|
pub fn get_sender_by_empfangsfrequenz(freq: f64) -> *mut Sender {
|
||||||
|
unsafe {
|
||||||
|
let mut current = SENDER_HEAD;
|
||||||
|
while !current.is_null() {
|
||||||
|
if (*current).empfangsfrequenz == freq {
|
||||||
|
return current;
|
||||||
|
}
|
||||||
|
current = (*current).next;
|
||||||
|
}
|
||||||
|
std::ptr::null_mut()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn sender_conceal(_spl: *mut u8, _len: i32, _priv: *mut c_void) {}
|
||||||
@@ -6,6 +6,10 @@ mod amps;
|
|||||||
mod libmobile_sender;
|
mod libmobile_sender;
|
||||||
mod iir_filter;
|
mod iir_filter;
|
||||||
mod samplerate;
|
mod samplerate;
|
||||||
|
mod emphasis;
|
||||||
|
mod jitter;
|
||||||
|
mod wave;
|
||||||
|
mod display;
|
||||||
|
|
||||||
fn main() -> Result<(), Box<dyn std::error::Error>> {
|
fn main() -> Result<(), Box<dyn std::error::Error>> {
|
||||||
let stdout = stdout();
|
let stdout = stdout();
|
||||||
|
|||||||
@@ -1,40 +1,149 @@
|
|||||||
struct SampleRate {
|
use crate::iir_filter::IIRFilter;
|
||||||
factor: f64,
|
|
||||||
filter_cutoff: f64,
|
type Sample = f64;
|
||||||
down: DownSampler,
|
|
||||||
up: UpSampler,
|
#[derive(Clone)]
|
||||||
|
pub struct SampleRate {
|
||||||
|
pub factor: f64,
|
||||||
|
pub filter_cutoff: f64,
|
||||||
|
pub down: DownSampler,
|
||||||
|
pub up: UpSampler,
|
||||||
}
|
}
|
||||||
|
|
||||||
struct DownSampler {
|
#[derive(Clone)]
|
||||||
lp: IIRFilter,
|
pub struct DownSampler {
|
||||||
last_sample: Sample,
|
pub lp: IIRFilter,
|
||||||
in_index: f64,
|
pub last_sample: Sample,
|
||||||
|
pub in_index: f64,
|
||||||
}
|
}
|
||||||
|
|
||||||
struct UpSampler {
|
#[derive(Clone)]
|
||||||
lp: IIRFilter,
|
pub struct UpSampler {
|
||||||
current_sample: Sample,
|
pub lp: IIRFilter,
|
||||||
last_sample: Sample,
|
pub current_sample: Sample,
|
||||||
in_index: f64,
|
pub last_sample: Sample,
|
||||||
|
pub in_index: f64,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl SampleRate {
|
impl SampleRate {
|
||||||
pub fn init(&mut self, low_samplerate: f64, high_samplerate: f64, filter_cutoff: f64) -> Result<(), i32> {
|
pub fn init(&mut self, low_samplerate: f64, high_samplerate: f64, filter_cutoff: f64) -> Result<(), i32> {
|
||||||
// Implementation goes here
|
// clear state
|
||||||
|
self.factor = 0.0;
|
||||||
|
self.filter_cutoff = 0.0;
|
||||||
|
|
||||||
Ok(())
|
self.factor = high_samplerate / low_samplerate;
|
||||||
// or Err(error_code) if there's an error
|
if self.factor < 1.0 {
|
||||||
}
|
eprintln!("Software error: Low sample rate must be lower than high sample rate, aborting!");
|
||||||
|
return Err(-1);
|
||||||
|
}
|
||||||
|
|
||||||
pub fn downsample(&mut self, state: SampleRate, samples: Sample, input_num: i32) -> Result<(), i32> {
|
self.filter_cutoff = filter_cutoff;
|
||||||
|
if self.filter_cutoff > 0.0 {
|
||||||
|
self.up.lp.lowpass_init(filter_cutoff, high_samplerate as i32, 2);
|
||||||
|
self.down.lp.lowpass_init(filter_cutoff, high_samplerate as i32, 2);
|
||||||
|
}
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn upsample_input_num(&mut self, state: SampleRate, output_num: i32) -> Result<(), i32> {
|
pub fn downsample_input_num(&mut self, output_num: i32) -> i32 {
|
||||||
Ok(())
|
let factor = self.factor;
|
||||||
|
let mut in_index = self.down.in_index;
|
||||||
|
let mut idx = 0;
|
||||||
|
|
||||||
|
for _ in 0..output_num {
|
||||||
|
in_index += factor;
|
||||||
|
idx = in_index as i32;
|
||||||
|
}
|
||||||
|
|
||||||
|
idx
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn upsample_output_num(&mut self, state: SampleRate, input_num: i32) -> Result<(), i32> {
|
pub fn downsample_output_num(&mut self, input_num: i32) -> i32 {
|
||||||
|
let factor = self.factor;
|
||||||
|
let mut in_index = self.down.in_index;
|
||||||
|
let mut output_num = 0;
|
||||||
|
let mut idx;
|
||||||
|
|
||||||
|
loop {
|
||||||
|
idx = in_index as i32;
|
||||||
|
if idx >= input_num {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
output_num += 1;
|
||||||
|
in_index += factor;
|
||||||
|
}
|
||||||
|
|
||||||
|
output_num
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn downsample(&mut self, samples: &mut [Sample], input_num: i32) -> i32 {
|
||||||
|
let factor = self.factor;
|
||||||
|
let mut output_num = 0;
|
||||||
|
let mut in_index = self.down.in_index;
|
||||||
|
let mut last_sample = self.down.last_sample;
|
||||||
|
let mut idx;
|
||||||
|
let mut diff;
|
||||||
|
|
||||||
|
// filter down if cutoff is set
|
||||||
|
if self.filter_cutoff > 0.0 {
|
||||||
|
self.down.lp.process(&mut samples[..input_num as usize]);
|
||||||
|
}
|
||||||
|
|
||||||
|
// allocate output buffer w/ safety margin
|
||||||
|
let output_size = ((input_num as f64) / factor + 0.5) as usize + 10;
|
||||||
|
let mut output = vec![0.0; output_size];
|
||||||
|
|
||||||
|
for i in 0.. {
|
||||||
|
idx = in_index as i32;
|
||||||
|
if idx >= input_num {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
diff = in_index - idx as f64;
|
||||||
|
if idx > 0 {
|
||||||
|
output[i] = samples[(idx-1) as usize] * (1.0 - diff) + samples[idx as usize] * diff;
|
||||||
|
} else {
|
||||||
|
output[i] = last_sample * (1.0 - diff) + samples[idx as usize] * diff;
|
||||||
|
}
|
||||||
|
output_num += 1;
|
||||||
|
in_index += factor;
|
||||||
|
}
|
||||||
|
|
||||||
|
// store last sample
|
||||||
|
if input_num > 0 {
|
||||||
|
self.down.last_sample = samples[(input_num -1) as usize];
|
||||||
|
}
|
||||||
|
|
||||||
|
// adjust in_index
|
||||||
|
in_index -= input_num as f64;
|
||||||
|
if (in_index as i32) < 0 {
|
||||||
|
in_index = 0.0;
|
||||||
|
}
|
||||||
|
self.down.in_index = in_index;
|
||||||
|
|
||||||
|
// copy output back to samples
|
||||||
|
for i in 0..output_num {
|
||||||
|
samples[i as usize] = output[i as usize];
|
||||||
|
}
|
||||||
|
|
||||||
|
output_num
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn upsample_input_num(&mut self, output_num: i32) -> i32 {
|
||||||
|
let factor = 1.0 / self.factor;
|
||||||
|
let mut in_index = self.up.in_index;
|
||||||
|
let mut idx = 0;
|
||||||
|
|
||||||
|
for _ in 0..output_num {
|
||||||
|
in_index += factor;
|
||||||
|
if in_index >= 1.0 {
|
||||||
|
idx += 1;
|
||||||
|
in_index -= 1.0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
idx
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn upsample_output_num(&mut self, input_num: i32) -> i32 {
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -54,7 +54,12 @@ impl SdrTransmitter {
|
|||||||
// Helper function to create Args for a specific device
|
// Helper function to create Args for a specific device
|
||||||
pub fn device_args(driver: &str, serial: Option<&str>) -> Args {
|
pub fn device_args(driver: &str, serial: Option<&str>) -> Args {
|
||||||
match serial {
|
match serial {
|
||||||
Some(serial) => Args::from(&format!("driver={},serial={}", driver, serial)),
|
Some(serial) => Args::from(format!("driver={},serial={}", driver, serial).as_str()),
|
||||||
None => Args::from(&format!("driver={}", driver)),
|
None => Args::from(format!("driver={}", driver).as_str()),
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn find_device() -> Result<Args, Box<dyn std::error::Error>> {
|
||||||
|
// Stub: return default device args
|
||||||
|
Ok(device_args("hackrf", None))
|
||||||
}
|
}
|
||||||
13
cellular/src/wave.rs
Normal file
13
cellular/src/wave.rs
Normal file
@@ -0,0 +1,13 @@
|
|||||||
|
// Stub for wave recording/playback
|
||||||
|
#[derive(Clone)]
|
||||||
|
pub struct WaveRec;
|
||||||
|
#[derive(Clone)]
|
||||||
|
pub struct WavePlay;
|
||||||
|
|
||||||
|
impl WaveRec {
|
||||||
|
pub fn new() -> Self { WaveRec }
|
||||||
|
}
|
||||||
|
|
||||||
|
impl WavePlay {
|
||||||
|
pub fn new() -> Self { WavePlay }
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user