port more from osmocom

This commit is contained in:
2025-11-09 22:43:24 -05:00
parent 48dff5586c
commit be7b905507
10 changed files with 569 additions and 70 deletions

View File

@@ -1,17 +1,19 @@
const FSK_MAX_BITS: i32 = 1032;
enum DSPMode {
OFF, // Channel not active
AUDIO_RX_AUDIO_TX, // stream audio
AUDIO_RX_FRAME_TX, // stream audio and send frames
AUDIO_RX_SILENCE_TX, // stream audio and send silence
FRAME_RX_FRAME_TX, // send and receive frames
AudioRxAudioTx, // stream audio
AudioRxFrameTx, // stream audio and send frames
AudioRxSilenceTx, // stream audio and send silence
FrameRxFrameTx, // send and receive frames
}
enum AMPSChannelType {
CC, // control channel
PC, // paging channel
CC_PC, // combined control+paging
CcPc, // combined control+paging
VC, // voice channel
CC_PC_VC, // combined control+paging+voice
CcPcVc, // combined control+paging+voice
}
enum AMPSState {
@@ -20,7 +22,7 @@ enum AMPSState {
BUSY, // channel busy (call in progress)
}
enum FSK_RX_Sync {
enum FSKRxSync {
NONE, // not in sync, waiting for valid dotting sequence
DOTTING, // received a valid dotting sequence, check for sync sequence
POSITIVE, // valid sync, read all bits from the frame

13
cellular/src/display.rs Normal file
View 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
View 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);
}
}
}

View File

@@ -1,36 +1,130 @@
struct IIRFilter {
iter: i32,
a0: f64,
a1: f64,
a2: f64,
b1: f64,
b2: f64,
z1: f64,
z2: f64,
type Sample = f64;
#[derive(Clone)]
pub struct IIRFilter {
pub iter: i32,
pub a0: f64,
pub a1: f64,
pub a2: 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 {
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) {
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) {
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
View 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
}
}

View File

@@ -1,16 +1,34 @@
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 {
NONE = 0,
TONE,
NOTONE,
POSITIVE,
NEGATIVE,
None = 0,
Tone,
NoTone,
Positive,
Negative,
}
#[derive(Clone)]
struct Sender {
// System Info
channel: char,
send_freq: f64,
recv_freq: f64,
next: *mut Sender,
slave: *mut Sender,
master: *mut Sender,
// System info
kanal: String,
sendefrequenz: f64,
empfangsfrequenz: f64,
ruffrequenz: f64,
// FM/AM levels
am: i32,
@@ -21,25 +39,168 @@ struct Sender {
max_display: f64,
// Audio
audio: void,
device: char,
audio_open: void,
audio_start: i32,
audio_close: void,
audio_write: i32,
audio_get_tosend: i32,
audio: *mut c_void,
device: String,
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: Option<fn(*mut c_void) -> i32>,
audio_close: Option<fn(*mut c_void)>,
audio_write: Option<fn(*mut c_void, *mut *mut Sample, *mut *mut u8, i32, *mut PagingSignal, *mut i32, i32) -> 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_state: SampleRate,
srstate: SampleRate,
rx_gain: f64,
tx_gain: f64,
pre_emphasis: i32,
de_emphasis: i32,
epmphasis_state: EmphasisState,
estate: Emphasis,
// Loopback
// Loopback test
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) {}

View File

@@ -6,6 +6,10 @@ mod amps;
mod libmobile_sender;
mod iir_filter;
mod samplerate;
mod emphasis;
mod jitter;
mod wave;
mod display;
fn main() -> Result<(), Box<dyn std::error::Error>> {
let stdout = stdout();

View File

@@ -1,40 +1,149 @@
struct SampleRate {
factor: f64,
filter_cutoff: f64,
down: DownSampler,
up: UpSampler,
use crate::iir_filter::IIRFilter;
type Sample = f64;
#[derive(Clone)]
pub struct SampleRate {
pub factor: f64,
pub filter_cutoff: f64,
pub down: DownSampler,
pub up: UpSampler,
}
struct DownSampler {
lp: IIRFilter,
last_sample: Sample,
in_index: f64,
#[derive(Clone)]
pub struct DownSampler {
pub lp: IIRFilter,
pub last_sample: Sample,
pub in_index: f64,
}
struct UpSampler {
lp: IIRFilter,
current_sample: Sample,
last_sample: Sample,
in_index: f64,
#[derive(Clone)]
pub struct UpSampler {
pub lp: IIRFilter,
pub current_sample: Sample,
pub last_sample: Sample,
pub in_index: f64,
}
impl SampleRate {
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(())
// or Err(error_code) if there's an error
self.factor = high_samplerate / low_samplerate;
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(())
}
pub fn upsample_input_num(&mut self, state: SampleRate, output_num: i32) -> Result<(), i32> {
Ok(())
pub fn downsample_input_num(&mut self, output_num: i32) -> i32 {
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;
}
pub fn upsample_output_num(&mut self, state: SampleRate, input_num: i32) -> Result<(), i32> {
idx
}
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(())
}

View File

@@ -54,7 +54,12 @@ impl SdrTransmitter {
// Helper function to create Args for a specific device
pub fn device_args(driver: &str, serial: Option<&str>) -> Args {
match serial {
Some(serial) => Args::from(&format!("driver={},serial={}", driver, serial)),
None => Args::from(&format!("driver={}", driver)),
Some(serial) => Args::from(format!("driver={},serial={}", driver, serial).as_str()),
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
View 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 }
}