Compare commits

...

2 Commits

Author SHA1 Message Date
7ef059fa5b play with FSK more 2025-03-25 21:49:51 -04:00
fd5feaca3f add nix flake 2025-03-25 21:49:41 -04:00
7 changed files with 176 additions and 15 deletions

1
.envrc Normal file
View File

@@ -0,0 +1 @@
use flake

3
.gitignore vendored
View File

@@ -8,3 +8,6 @@ target/
# MSVC Windows builds of rustc generate these, which store debugging information
*.pdb
# Direnv
.direnv/

View File

@@ -1,20 +1,34 @@
use cpal::{StreamConfig, traits::{DeviceTrait, HostTrait}};
use cpal::traits::{DeviceTrait, HostTrait, StreamTrait};
use std::sync::{Arc, Mutex};
pub fn play_audio(signal: &[f32]) -> Result<(), Box<dyn std::error::Error>> {
let host = cpal::default_host();
let device = host.default_output_device()?.config();
let device = host.default_output_device().ok_or("No output device found")?;
let config = device.default_output_config()?.config();
let signal = Arc::new(Mutex::new(signal.to_vec()));
let singal_clone = Arc::clone(&signal);
let mut index = 0;
let stream = device.build_output_stream(
&config,
move |data: &mut [f32], _: &cpal::OutputCallbackInfo| {
data.copy_from_slice(&signal[..data.len()]);
let signal = singal_clone.lock().unwrap();
for sample in data.iter_mut() {
*sample = if index < signal.len() {
signal[index]
} else {
0.0
};
index += 1;
}
},
|err| eprintln!("Error: {:?}", err),
None
)?;
stream.play()?;
std::thread::sleep(std::time::Duration::from_secs(5));
std::thread::sleep(std::time::Duration::from_secs(1));
Ok(())
}

View File

@@ -1,20 +1,43 @@
use std::f32::consts::PI;
use dasp_signal::Signal;
const SAMPLE_RATE: f32 = 44100.0;
const BAUD_RATE: f32 = 1200.0;
const FREQ_MARK: f32 = 1200.0;
const FREQ_SPACE: f32 = 2200.0;
pub fn fsk_modulate(bits: &[u8]) -> Vec<f32> {
pub fn fsk_modulate(data: &[u8]) -> Vec<f32> {
let samples_per_bit = (SAMPLE_RATE / BAUD_RATE) as usize;
let mut signal = Vec::new();
for &bit in bits {
let freq = if bit == 1 { FREQ_MARK } else { FREQ_SPACE };
for i in 0..samples_per_bit {
let t = i as f32 / SAMPLE_RATE;
signal.push((2.0 * PI * freq * t).sin());
for &byte in data {
for i in (0..8).rev() {
let bit = (byte >> i) & 1;
let freq = if bit == 1 { FREQ_MARK } else { FREQ_SPACE };
for j in 0..samples_per_bit {
let t = (signal.len() + j) as f32 / SAMPLE_RATE;
signal.push((2.0 * PI * freq * t).sin());
}
}
}
signal
}
pub fn afsk_modulate(data: &[u8], carrier_freq: f32) -> Vec<f32> {
let samples_per_bit = (SAMPLE_RATE / BAUD_RATE) as usize;
let mut signal = Vec::new();
for &byte in data {
for i in (0..8).rev() {
let bit = (byte >> i) & 1;
let freq = if bit == 1 { FREQ_MARK } else { FREQ_SPACE };
for j in 0..samples_per_bit {
let t = (signal.len() + j) as f32 / SAMPLE_RATE;
let fsk_wave = (2.0 * PI * freq * t).sin();
let carrier_wave = (2.0 * PI * carrier_freq * t).sin();
signal.push(fsk_wave * carrier_wave); // Modulate FSK onto the carrier
}
}
}
signal

View File

@@ -1,4 +1,5 @@
use fsk::fsk_modulate;
use audio::play_audio;
use fsk::afsk_modulate;
use text_io::scan;
use std::io;
@@ -15,9 +16,14 @@ fn main() -> Result<(), io::Error> {
match a {
1 => {
println!("1 has been pressed! No test tone implemented yet");
let tone: signal;
tone = fsk_modulate([0x25, 0x08, 0x00, 0x25, 0xA0]);
play_audio(tone);
let tone: Vec<f32>;
tone = afsk_modulate(&[
0b10101010, 0b01010101, 0b10101010, 0b01010101, 0b10101010, 0b01010101, 0b10101010, 0b01010101, 0b10101010, 0b01010101,
0b1000101,0b11111111,0b1000101,0b11111111,0b11111111,0b1000101,0b11111111,0b11111111,0b1000101,0b11111111,0b11111111,
], 1700.0);
let repeated_tone: Vec<f32> = tone.iter().cloned().cycle().take(tone.len() * 10).collect();
play_audio(&repeated_tone);
return main();
},
0 => {

82
flake.lock generated Normal file
View File

@@ -0,0 +1,82 @@
{
"nodes": {
"flake-utils": {
"inputs": {
"systems": "systems"
},
"locked": {
"lastModified": 1731533236,
"narHash": "sha256-l0KFg5HjrsfsO/JpG+r7fRrqm12kzFHyUHqHCVpMMbI=",
"owner": "numtide",
"repo": "flake-utils",
"rev": "11707dc2f618dd54ca8739b309ec4fc024de578b",
"type": "github"
},
"original": {
"owner": "numtide",
"repo": "flake-utils",
"type": "github"
}
},
"nixpkgs": {
"locked": {
"lastModified": 1742669843,
"narHash": "sha256-G5n+FOXLXcRx+3hCJ6Rt6ZQyF1zqQ0DL0sWAMn2Nk0w=",
"owner": "NixOS",
"repo": "nixpkgs",
"rev": "1e5b653dff12029333a6546c11e108ede13052eb",
"type": "github"
},
"original": {
"owner": "NixOS",
"ref": "nixos-unstable",
"repo": "nixpkgs",
"type": "github"
}
},
"root": {
"inputs": {
"flake-utils": "flake-utils",
"nixpkgs": "nixpkgs",
"rust-overlay": "rust-overlay"
}
},
"rust-overlay": {
"inputs": {
"nixpkgs": [
"nixpkgs"
]
},
"locked": {
"lastModified": 1742870002,
"narHash": "sha256-eQnw8ufyLmrboODU8RKVNh2Mv7SACzdoFrRUV5zdNNE=",
"owner": "oxalica",
"repo": "rust-overlay",
"rev": "b4c18f262dbebecb855136c1ed8047b99a9c75b6",
"type": "github"
},
"original": {
"owner": "oxalica",
"repo": "rust-overlay",
"type": "github"
}
},
"systems": {
"locked": {
"lastModified": 1681028828,
"narHash": "sha256-Vy1rq5AaRuLzOxct8nz4T6wlgyUR7zLU309k9mBC768=",
"owner": "nix-systems",
"repo": "default",
"rev": "da67096a3b9bf56a91d16901293e51ba5b49a27e",
"type": "github"
},
"original": {
"owner": "nix-systems",
"repo": "default",
"type": "github"
}
}
},
"root": "root",
"version": 7
}

32
flake.nix Normal file
View File

@@ -0,0 +1,32 @@
{
inputs = {
nixpkgs.url = "github:NixOS/nixpkgs/nixos-unstable";
flake-utils.url = "github:numtide/flake-utils";
rust-overlay = {
url = "github:oxalica/rust-overlay";
inputs = {
nixpkgs.follows = "nixpkgs";
};
};
};
outputs = { self, nixpkgs, flake-utils, rust-overlay }:
flake-utils.lib.eachDefaultSystem (system:
let
overlays = [ (import rust-overlay ) ];
pkgs = import nixpkgs {
inherit system overlays;
};
in
with pkgs;
{
devShells.default = mkShell {
buildInputs = [
rust-bin.stable.latest.default
pkgs.pkg-config
pkgs.alsa-lib
];
};
}
);
}