diff --git a/.vscode/.server-controller-port.log b/.vscode/.server-controller-port.log index 493b0c5..4aa429f 100644 --- a/.vscode/.server-controller-port.log +++ b/.vscode/.server-controller-port.log @@ -1,5 +1,5 @@ { "port": 13452, - "time": 1737492785115, + "time": 1737621156918, "version": "0.0.3" } \ No newline at end of file diff --git a/.vscode/settings.json b/.vscode/settings.json index dec1312..0c55742 100644 --- a/.vscode/settings.json +++ b/.vscode/settings.json @@ -11,6 +11,7 @@ "math.h": "c", "options.h": "c", "random": "c", - "__locale": "c" + "__locale": "c", + "complex": "c" } } \ No newline at end of file diff --git a/README.md b/README.md index 77b7e06..25ba6ed 100644 --- a/README.md +++ b/README.md @@ -7,40 +7,25 @@ STCode is a simple stereo encoder for FM, it uses pasimple and math to: - Generate the stereo pilot in phase to the stereo subcarrier - Generate the stereo diffrence signal using DSB-SC ((L-R)/2) -All that in about 3.5% cpu usage on a RPI-5 (lpf makes it 10, but stereo tool has 3 threads which do 100% cpu usage anyway, one 200)! +All that in about 3.5% cpu usage on a RPI-5 (stereo tool has 3 threads which do 100% cpu usage anyway, one 200) Also nearly no latency, not like Stereo Tool (or mpxgen which doesn't even work) -As far as i've tested it (29-31 december) it's been fine but after a fix it was great, so i'd redecommend you +As far as i've tested it (29-31 december) it's been fine but after a fix it was great, so i'd recommend this to you Also i'd recommend to use the SSB version because it's more spectrum effiecent - -# SSB-STCode -This is a version of the stereo code but instead of DSB-SC it transmits some kind of VSG (mostly USB with a bit of LSB), about 600 hz of usb is left, just as god intended (Hilbert isn't perfect so i got some usb but managed to turn it into more into LSB) - -This also has a cpu usage of 20% with lpf, but goes to 13-15% without the lpf +but SSB has slightly more cpu usage # PSTCode This is a yet another version of a Stereo encoder, however for the OIRT band which is in use in Russia, Belarus and other countries Haven't tested it nor plan to -# CrosbySTCode -This is a stereo coder however with a diffrent system, let me yap some: -In the 1950-1960s the FCC had to decide between two stereo coding systems, we had the Zenith/GE system and the Crosby system, what was the diffrence? -The Zenith system had a 19 khz pilot and a 38 khz dsb-sc modulated stereo l-r signal, sounds familliar? yeah that's why you haven't heard of the crosby system -The crosby system on the other hand had a (better) decision of modulation the l-r signal into 50 khz with FM, why was it rejected? becuase of SCA, 67 and 41 khz were used up by stereo, 41 khz was also used up on the Zenith system but who cares - -Also it doesnsn't sound bad, how may ask where did i find a decoder for it? Made it myself in GNU radio, it's even easier, if they chose 35 khz instead of 50, then we'd be using this, also i like this one because it has FM, not AM so if some idiot has a transmitter with hardware pre-emphasis then the stereo won't be affected by it - # SCAMod SCAMod is a simple FM modulator which can be used to modulate a secondary audio stream, has similiar cpu usage and latency as STCode Has a fine quality, but as it goes for 12 khz fm signals -# StereoSCAMod -Stereo SCA, like normal SCA but encodes L-R onto 80 khz, only demodulator of this right now is gnu radio - # MonoPass want to keep mono for a reason but have the lpf and preemphasis, do so diff --git a/lib/filters.c b/lib/filters.c index c891ad3..e40b065 100644 --- a/lib/filters.c +++ b/lib/filters.c @@ -1,6 +1,11 @@ #include "filters.h" -void init_rc(ResistorCapacitor *rc, float tau, float sample_rate) { +void init_rc(ResistorCapacitor *rc, float alpha) { + rc->prev_sample = 0.0f; + rc->alpha = alpha; +} + +void init_rc_tau(ResistorCapacitor *rc, float tau, float sample_rate) { rc->prev_sample = 0.0f; rc->alpha = exp(-1 / (tau * sample_rate)); } @@ -8,15 +13,16 @@ void init_rc(ResistorCapacitor *rc, float tau, float sample_rate) { float apply_pre_emphasis(ResistorCapacitor *rc, float sample) { float audio = sample-rc->alpha*rc->prev_sample; rc->prev_sample = audio; - return audio*2; + return audio; } void init_low_pass_filter(ResistorCapacitor *rc, float cutoff_frequency, float sample_rate) { - init_rc(&rc, (-1 / (sample_rate * log(sample_rate/(sample_rate+(1/(M_2PI*cutoff_frequency)))))), sample_rate); + float RC = 1.0f / (M_2PI * cutoff_frequency); + init_rc(&rc, 1.0f / (1.0f + sample_rate * RC)); } float apply_low_pass_filter(ResistorCapacitor *rc, float sample) { - float output = rc->alpha*sample+(1-rc->alpha)*rc->prev_sample; + float output = rc->alpha*sample+(1.0f-rc->alpha)*rc->prev_sample; rc->prev_sample = output; return output; } diff --git a/lib/filters.h b/lib/filters.h index d67f0f6..58137d0 100644 --- a/lib/filters.h +++ b/lib/filters.h @@ -5,21 +5,18 @@ #include #include "constants.h" -#define FIR_PHASES 32 -#define FIR_TAPS 32 - typedef struct { float alpha; float prev_sample; } ResistorCapacitor; -void init_rc(ResistorCapacitor *pe, float tau, float sample_rate); +void init_rc(ResistorCapacitor *pe, float alpha); +void init_rc_tau(ResistorCapacitor *pe, float tau, float sample_rate); float apply_pre_emphasis(ResistorCapacitor *pe, float sample); void init_low_pass_filter(ResistorCapacitor *lp, float cutoff_frequency, float sample_rate); float apply_low_pass_filter(ResistorCapacitor *lp, float sample); - typedef struct { float *buffer; int write_idx; // Write position diff --git a/src/crosby_stereo_coder.c b/satire/crosby_stereo_coder.c similarity index 97% rename from src/crosby_stereo_coder.c rename to satire/crosby_stereo_coder.c index 8f233eb..8006eea 100644 --- a/src/crosby_stereo_coder.c +++ b/satire/crosby_stereo_coder.c @@ -123,11 +123,11 @@ int main() { init_oscillator(&osc, 50000.0, SAMPLE_RATE); #ifdef PREEMPHASIS ResistorCapacitor preemp_l, preemp_r; - init_rc(&preemp_l, PREEMPHASIS_TAU, SAMPLE_RATE); - init_rc(&preemp_r, PREEMPHASIS_TAU, SAMPLE_RATE); + init_rc_tau(&preemp_l, PREEMPHASIS_TAU, SAMPLE_RATE); + init_rc_tau(&preemp_r, PREEMPHASIS_TAU, SAMPLE_RATE); #endif #ifdef LPF - LowPassFilter lpf_l, lpf_r; + ResistorCapacitor lpf_l, lpf_r; init_low_pass_filter(&lpf_l, LPF_CUTOFF, SAMPLE_RATE); init_low_pass_filter(&lpf_r, LPF_CUTOFF, SAMPLE_RATE); #endif diff --git a/src/stereo_sca_mod.c b/satire/stereo_sca_mod.c similarity index 97% rename from src/stereo_sca_mod.c rename to satire/stereo_sca_mod.c index e51f9b2..7b13e93 100644 --- a/src/stereo_sca_mod.c +++ b/satire/stereo_sca_mod.c @@ -125,11 +125,11 @@ int main() { init_fm_modulator(&mod_stereo, 80000, 6000, SAMPLE_RATE); #ifdef PREEMPHASIS ResistorCapacitor preemp_l, preemp_r; - init_rc(&preemp_l, PREEMPHASIS_TAU, SAMPLE_RATE); - init_rc(&preemp_r, PREEMPHASIS_TAU, SAMPLE_RATE); + init_rc_tau(&preemp_l, PREEMPHASIS_TAU, SAMPLE_RATE); + init_rc_tau(&preemp_r, PREEMPHASIS_TAU, SAMPLE_RATE); #endif #ifdef LPF - LowPassFilter lpf_l, lpf_r; + ResistorCapacitor lpf_l, lpf_r; init_low_pass_filter(&lpf_l, LPF_CUTOFF, SAMPLE_RATE); init_low_pass_filter(&lpf_r, LPF_CUTOFF, SAMPLE_RATE); #endif diff --git a/src/mono_passthrough.c b/src/mono_passthrough.c index 31e2a5f..64592f2 100644 --- a/src/mono_passthrough.c +++ b/src/mono_passthrough.c @@ -31,7 +31,7 @@ volatile sig_atomic_t to_run = 1; -float clip(float sample) { +float hard_clip(float sample) { if (sample > CLIPPER_THRESHOLD) { return CLIPPER_THRESHOLD; // Clip to the upper threshold } else if (sample < -CLIPPER_THRESHOLD) { @@ -105,10 +105,10 @@ int main() { #ifdef PREEMPHASIS ResistorCapacitor preemp; - init_rc(&preemp, PREEMPHASIS_TAU, SAMPLE_RATE); + init_rc_tau(&preemp, PREEMPHASIS_TAU, SAMPLE_RATE); #endif #ifdef LPF - LowPassFilter lpf; + ResistorCapacitor lpf; init_low_pass_filter(&lpf, LPF_CUTOFF, SAMPLE_RATE); #endif @@ -132,17 +132,17 @@ int main() { #ifdef LPF float lowpassed = apply_low_pass_filter(&lpf, in); float preemphasized = apply_pre_emphasis(&preemp, lowpassed); - float current_input = clip(preemphasized); + float current_input = hard_clip(preemphasized); #else float preemphasized = apply_pre_emphasis(&preemp, in); - float current_input = clip(preemphasized); + float current_input = hard_clip(preemphasized); #endif #else #ifdef LPF float lowpassed = apply_low_pass_filter(&lpf, in); - float current_input = clip(lowpassed); + float current_input = hard_clip(lowpassed); #else - float current_input = clip(in); + float current_input = hard_clip(in); #endif #endif diff --git a/src/options.h b/src/options.h index 9bc2866..4671b01 100644 --- a/src/options.h +++ b/src/options.h @@ -1,5 +1,5 @@ #define PREEMPHASIS -// #define LPF +#define LPF #define buffer_maxlength 12288 #define buffer_tlength_fragsize 8192 diff --git a/src/polar_stereo_coder.c b/src/polar_stereo_coder.c index 2b52852..60717ee 100644 --- a/src/polar_stereo_coder.c +++ b/src/polar_stereo_coder.c @@ -33,7 +33,7 @@ volatile sig_atomic_t to_run = 1; -float clip(float sample) { +float hard_clip(float sample) { if (sample > CLIPPER_THRESHOLD) { return CLIPPER_THRESHOLD; // Clip to the upper threshold } else if (sample < -CLIPPER_THRESHOLD) { @@ -122,11 +122,11 @@ int main() { init_oscillator(&stereo_osc, 31250.0, SAMPLE_RATE); #ifdef PREEMPHASIS ResistorCapacitor preemp_l, preemp_r; - init_rc(&preemp_l, PREEMPHASIS_TAU, SAMPLE_RATE); - init_rc(&preemp_r, PREEMPHASIS_TAU, SAMPLE_RATE); + init_rc_tau(&preemp_l, PREEMPHASIS_TAU, SAMPLE_RATE); + init_rc_tau(&preemp_r, PREEMPHASIS_TAU, SAMPLE_RATE); #endif #ifdef LPF - LowPassFilter lpf_l, lpf_r; + ResistorCapacitor lpf_l, lpf_r; init_low_pass_filter(&lpf_l, LPF_CUTOFF, SAMPLE_RATE); init_low_pass_filter(&lpf_r, LPF_CUTOFF, SAMPLE_RATE); #endif @@ -156,23 +156,23 @@ int main() { float lowpassed_right = apply_low_pass_filter(&lpf_r, r_in); float preemphasized_left = apply_pre_emphasis(&preemp_l, lowpassed_left); float preemphasized_right = apply_pre_emphasis(&preemp_r, lowpassed_right); - float current_left_input = clip(preemphasized_left); - float current_right_input = clip(preemphasized_right); + float current_left_input = hard_clip(preemphasized_left); + float current_right_input = hard_clip(preemphasized_right); #else float preemphasized_left = apply_pre_emphasis(&preemp_l, l_in); float preemphasized_right = apply_pre_emphasis(&preemp_r, r_in); - float current_left_input = clip(preemphasized_left); - float current_right_input = clip(preemphasized_right); + float current_left_input = hard_clip(preemphasized_left); + float current_right_input = hard_clip(preemphasized_right); #endif #else #ifdef LPF float lowpassed_left = apply_low_pass_filter(&lpf_l, l_in); float lowpassed_right = apply_low_pass_filter(&lpf_r, r_in); - float current_left_input = clip(lowpassed_left); - float current_right_input = clip(lowpassed_right); + float current_left_input = hard_clip(lowpassed_left); + float current_right_input = hard_clip(lowpassed_right); #else - float current_left_input = clip(l_in); - float current_right_input = clip(r_in); + float current_left_input = hard_clip(l_in); + float current_right_input = hard_clip(r_in); #endif #endif @@ -180,10 +180,10 @@ int main() { float stereo = (current_left_input - current_right_input) / 2.0f; // Also Stereo to Mono but a bit diffrent float stereo_carrier = get_oscillator_sin_sample(&stereo_osc); - // 14 db is somewhere around 20% of a 1 volt signal + // -14 db is somewhere around 20% of a 1 volt signal mpx[i] = mono * MONO_VOLUME + - ((stereo+0.2) * stereo_carrier)*STEREO_VOLUME; + ((stereo+0.2) * stereo_carrier)*STEREO_VOLUME; // the 0.2 add DC, you know what happens then? Carrier wave } if (pa_simple_write(output_device, mpx, sizeof(mpx), &pulse_error) < 0) { diff --git a/src/sca_mod.c b/src/sca_mod.c index 876386f..5a668e6 100644 --- a/src/sca_mod.c +++ b/src/sca_mod.c @@ -36,7 +36,7 @@ volatile sig_atomic_t to_run = 1; -float clip(float sample) { +float hard_clip(float sample) { if (sample > CLIPPER_THRESHOLD) { return CLIPPER_THRESHOLD; // Clip to the upper threshold } else if (sample < -CLIPPER_THRESHOLD) { @@ -113,10 +113,10 @@ int main() { init_fm_modulator(&mod, FREQUENCY, DEVIATION, SAMPLE_RATE); #ifdef PREEMPHASIS ResistorCapacitor preemp; - init_rc(&preemp, PREEMPHASIS_TAU, SAMPLE_RATE); + init_rc_tau(&preemp, PREEMPHASIS_TAU, SAMPLE_RATE); #endif #ifdef LPF - LowPassFilter lpf; + ResistorCapacitor lpf; init_low_pass_filter(&lpf, LPF_CUTOFF, SAMPLE_RATE); #endif @@ -140,17 +140,17 @@ int main() { #ifdef LPF float lowpassed = apply_low_pass_filter(&lpf, in); float preemphasized = apply_pre_emphasis(&preemp, lowpassed); - float current_input = clip(preemphasized); + float current_input = hard_clip(preemphasized); #else float preemphasized = apply_pre_emphasis(&preemp, in); - float current_input = clip(preemphasized); + float current_input = hard_clip(preemphasized); #endif #else #ifdef LPF float lowpassed = apply_low_pass_filter(&lpf, in); - float current_input = clip(lowpassed); + float current_input = hard_clip(lowpassed); #else - float current_input = clip(in); + float current_input = hard_clip(in); #endif #endif diff --git a/src/ssb_stereo_coder.c b/src/ssb_stereo_coder.c deleted file mode 100644 index 697932f..0000000 --- a/src/ssb_stereo_coder.c +++ /dev/null @@ -1,216 +0,0 @@ -#include -#include -#include -#include -#include -#include -#include -#include - -#include "../lib/constants.h" -#include "../lib/oscillator.h" -#include "../lib/filters.h" -#include "../lib/hilbert.h" - -#include "options.h" -//#define USB - -#define SAMPLE_RATE 192000 // Don't go lower than 108 KHz, becuase it (53000*2) and (38000+15000) - -#define INPUT_DEVICE "real_real_tx_audio_input.monitor" -#define OUTPUT_DEVICE "alsa_output.platform-soc_sound.stereo-fallback" -#define BUFFER_SIZE 512 -#define CLIPPER_THRESHOLD 0.525 // Adjust this as needed - -#define MONO_VOLUME 0.45f // L+R Signal -#define PILOT_VOLUME 0.09f // 19 KHz Pilot -#define STEREO_VOLUME 0.45f // L-R signal possibly can be set to .9 because im not sure if usb will be 2 times stronger than dsb-sc - -#ifdef PREEMPHASIS -#define PREEMPHASIS_TAU 0.00005 // 50 microseconds, use 0.000075 if in america -#endif - -#ifdef LPF -#define LPF_CUTOFF 15000 -#endif - -volatile sig_atomic_t to_run = 1; - -float clip(float sample) { - if (sample > CLIPPER_THRESHOLD) { - return CLIPPER_THRESHOLD; // Clip to the upper threshold - } else if (sample < -CLIPPER_THRESHOLD) { - return -CLIPPER_THRESHOLD; // Clip to the lower threshold - } else { - return sample; // No clipping - } -} - -void uninterleave(const float *input, float *left, float *right, size_t num_samples) { - // For stereo, usually it is like this: LEFT RIGHT LEFT RIGHT LEFT RIGHT so this is used to get LEFT LEFT LEFT and RIGHT RIGHT RIGHT - for (size_t i = 0; i < num_samples/2; i++) { - left[i] = input[i * 2]; - right[i] = input[i * 2 + 1]; - } -} - -static void stop(int signum) { - (void)signum; - printf("\nReceived stop signal. Cleaning up...\n"); - to_run = 0; -} - -int main() { - printf("SSB-STCode : Stereo encoder made by radio95 (with help of ChatGPT and Claude, thanks!)\n"); - // Define formats and buffer atributes - pa_sample_spec stereo_format = { - .format = PA_SAMPLE_FLOAT32NE, //Float32 NE, or Float32 Native Endian, the float in c uses the endianess of your pc, or native endian, and float is float32, and double is float64 - .channels = 2, - .rate = SAMPLE_RATE // Same sample rate makes it easy, leave the resampling to pipewire, it should know better - }; - pa_sample_spec mono_format = { - .format = PA_SAMPLE_FLOAT32NE, - .channels = 1, - .rate = SAMPLE_RATE - }; - - pa_buffer_attr input_buffer_atr = { - .maxlength = buffer_maxlength, - .fragsize = buffer_tlength_fragsize - }; - pa_buffer_attr output_buffer_atr = { - .maxlength = buffer_maxlength, - .tlength = buffer_tlength_fragsize, - .prebuf = buffer_prebuf - }; - - printf("Connecting to input device... (%s)\n", INPUT_DEVICE); - - pa_simple *input_device = pa_simple_new( - NULL, - "StereoEncoder", - PA_STREAM_RECORD, - INPUT_DEVICE, - "Audio Input", - &stereo_format, - NULL, - &input_buffer_atr, - NULL - ); - if (!input_device) { - fprintf(stderr, "Error: cannot open input device.\n"); - return 1; - } - - printf("Connecting to output device... (%s)\n", OUTPUT_DEVICE); - - pa_simple *output_device = pa_simple_new( - NULL, - "StereoEncoder", - PA_STREAM_PLAYBACK, - OUTPUT_DEVICE, - "MPX", - &mono_format, - NULL, - &output_buffer_atr, - NULL - ); - if (!output_device) { - fprintf(stderr, "Error: cannot open output device.\n"); - pa_simple_free(input_device); - return 1; - } - - Oscillator pilot_osc; - init_oscillator(&pilot_osc, 19000.0, SAMPLE_RATE); // Pilot, it's there to indicate stereo and as a refrence signal with the stereo carrier - HilbertTransformer hilbert; - init_hilbert(&hilbert); - DelayLine monoDelay; - init_delay_line(&monoDelay, 99); -#ifdef PREEMPHASIS - ResistorCapacitor preemp_l, preemp_r; - init_rc(&preemp_l, PREEMPHASIS_TAU, SAMPLE_RATE); - init_rc(&preemp_r, PREEMPHASIS_TAU, SAMPLE_RATE); -#endif -#ifdef LPF - LowPassFilter lpf_l, lpf_r; - init_low_pass_filter(&lpf_l, LPF_CUTOFF, SAMPLE_RATE); - init_low_pass_filter(&lpf_r, LPF_CUTOFF, SAMPLE_RATE); -#endif - - signal(SIGINT, stop); - signal(SIGTERM, stop); - - int pulse_error; - float input[BUFFER_SIZE*2]; // Input from device, interleaved stereo - float left[BUFFER_SIZE+64], right[BUFFER_SIZE+64]; // Audio, same thing as in input but ininterleaved, ai told be there could be a buffer overflow here - float mpx[BUFFER_SIZE]; // MPX, this goes to the output - while (to_run) { - if (pa_simple_read(input_device, input, sizeof(input), &pulse_error) < 0) { - fprintf(stderr, "Error reading from input device: %s\n", pa_strerror(pulse_error)); - to_run = 0; - break; - } - uninterleave(input, left, right, BUFFER_SIZE*2); - - for (int i = 0; i < BUFFER_SIZE; i++) { - float sin38 = get_oscillator_sin_multiplier_ni(&pilot_osc, 2); - float cos38 = get_oscillator_cos_multiplier_ni(&pilot_osc, 2); // Stereo carrier should be a harmonic of the pilot which is in phase, best way to generate the harmonic is to multiply the pilot's phase by two, so it is mathematically impossible for them to not be in phase - float pilot = get_oscillator_sin_sample(&pilot_osc); // This is after because if it was before then the stereo would be out of phase by one increment, so [GET STEREO] ([GET PILOT] [INCREMENT PHASE]) - float l_in = left[i]; - float r_in = right[i]; - -#ifdef PREEMPHASIS -#ifdef LPF - float lowpassed_left = apply_low_pass_filter(&lpf_l, l_in); - float lowpassed_right = apply_low_pass_filter(&lpf_r, r_in); - float preemphasized_left = apply_pre_emphasis(&preemp_l, lowpassed_left); - float preemphasized_right = apply_pre_emphasis(&preemp_r, lowpassed_right); - float current_left_input = clip(preemphasized_left); - float current_right_input = clip(preemphasized_right); -#else - float preemphasized_left = apply_pre_emphasis(&preemp_l, l_in); - float preemphasized_right = apply_pre_emphasis(&preemp_r, r_in); - float current_left_input = clip(preemphasized_left); - float current_right_input = clip(preemphasized_right); -#endif -#else -#ifdef LPF - float lowpassed_left = apply_low_pass_filter(&lpf_l, l_in); - float lowpassed_right = apply_low_pass_filter(&lpf_r, r_in); - float current_left_input = clip(lowpassed_left); - float current_right_input = clip(lowpassed_right); -#else - float current_left_input = clip(l_in); - float current_right_input = clip(r_in); -#endif -#endif - - float mono = (current_left_input + current_right_input) / 2.0f; // Stereo to Mono - - float stereo = (current_left_input - current_right_input) / 2.0f; // Also Stereo to Mono but a bit diffrent - float stereo_i, stereo_q; - apply_hilbert(&hilbert, stereo, &stereo_i, &stereo_q); // I/Q, the Quadrature data is 90 degrees apart from the In-phase data -#ifdef USB - float signal = (stereo_i*cos38+stereo_q*(sin38*0.775f)); // Compute LSB/USB, as the Hilbert isn't perfect, i'll have to a bit silence down the Q carrier in order to make it better, also, it is just perfect as FM Stereo LSB shouldn't be fully LSB -#else - float signal = (stereo_i*cos38-stereo_q*(sin38*0.775f)); // Compute LSB/USB, as the Hilbert isn't perfect, i'll have to a bit silence down the Q carrier in order to make it better, also, it is just perfect as FM Stereo LSB shouldn't be fully LSB -#endif - - mpx[i] = delay_line(&monoDelay, mono) * MONO_VOLUME + - pilot * PILOT_VOLUME + - signal*STEREO_VOLUME; - } - - if (pa_simple_write(output_device, mpx, sizeof(mpx), &pulse_error) < 0) { - fprintf(stderr, "Error writing to output device: %s\n", pa_strerror(pulse_error)); - to_run = 0; - break; - } - } - printf("Cleaning up...\n"); - pa_simple_free(input_device); - pa_simple_free(output_device); - exit_hilbert(&hilbert); - return 0; -} diff --git a/src/stereo_coder.c b/src/stereo_coder.c index ff46b38..8958706 100644 --- a/src/stereo_coder.c +++ b/src/stereo_coder.c @@ -7,11 +7,17 @@ #include #include +#include "options.h" + +//#define SSB +//#define USB + #include "../lib/constants.h" #include "../lib/oscillator.h" #include "../lib/filters.h" - -#include "options.h" +#ifdef SSB +#include "../lib/hilbert.h" +#endif #define SAMPLE_RATE 192000 // Don't go lower than 108 KHz, becuase it (53000*2) and (38000+15000) @@ -22,7 +28,7 @@ #define MONO_VOLUME 0.45f // L+R Signal #define PILOT_VOLUME 0.09f // 19 KHz Pilot -#define STEREO_VOLUME 0.45f // L-R signal +#define STEREO_VOLUME 0.45f // L-R signal possibly can be set to .9 because im not sure if usb will be 2 times stronger than dsb-sc #ifdef PREEMPHASIS #define PREEMPHASIS_TAU 0.00005 // 50 microseconds, use 0.000075 if in america @@ -34,7 +40,7 @@ volatile sig_atomic_t to_run = 1; -float clip(float sample) { +float hard_clip(float sample) { if (sample > CLIPPER_THRESHOLD) { return CLIPPER_THRESHOLD; // Clip to the upper threshold } else if (sample < -CLIPPER_THRESHOLD) { @@ -107,7 +113,7 @@ int main() { "StereoEncoder", PA_STREAM_PLAYBACK, OUTPUT_DEVICE, - "MPX", + "MPX Output", &mono_format, NULL, &output_buffer_atr, @@ -121,13 +127,20 @@ int main() { Oscillator pilot_osc; init_oscillator(&pilot_osc, 19000.0, SAMPLE_RATE); // Pilot, it's there to indicate stereo and as a refrence signal with the stereo carrier + +#ifdef SSB + HilbertTransformer hilbert; + init_hilbert(&hilbert); + DelayLine monoDelay; + init_delay_line(&monoDelay, 99); +#endif #ifdef PREEMPHASIS ResistorCapacitor preemp_l, preemp_r; - init_rc(&preemp_l, PREEMPHASIS_TAU, SAMPLE_RATE); - init_rc(&preemp_r, PREEMPHASIS_TAU, SAMPLE_RATE); + init_rc_tau(&preemp_l, PREEMPHASIS_TAU, SAMPLE_RATE); + init_rc_tau(&preemp_r, PREEMPHASIS_TAU, SAMPLE_RATE); #endif #ifdef LPF - LowPassFilter lpf_l, lpf_r; + ResistorCapacitor lpf_l, lpf_r; init_low_pass_filter(&lpf_l, LPF_CUTOFF, SAMPLE_RATE); init_low_pass_filter(&lpf_r, LPF_CUTOFF, SAMPLE_RATE); #endif @@ -148,7 +161,10 @@ int main() { uninterleave(input, left, right, BUFFER_SIZE*2); for (int i = 0; i < BUFFER_SIZE; i++) { - float stereo_carrier = get_oscillator_sin_multiplier_ni(&pilot_osc, 2); // Stereo carrier should be a harmonic of the pilot which is in phase, best way to generate the harmonic is to multiply the pilot's phase by two, so it is mathematically impossible for them to not be in phase + float sin38 = get_oscillator_sin_multiplier_ni(&pilot_osc, 2); // Stereo carrier should be a harmonic of the pilot which is in phase, best way to generate the harmonic is to multiply the pilot's phase by two, so it is mathematically impossible for them to not be in phase +#ifdef SSB + float cos38 = get_oscillator_cos_multiplier_ni(&pilot_osc, 2); +#endif float pilot = get_oscillator_sin_sample(&pilot_osc); // This is after because if it was before then the stereo would be out of phase by one increment, so [GET STEREO] ([GET PILOT] [INCREMENT PHASE]) float l_in = left[i]; float r_in = right[i]; @@ -159,32 +175,45 @@ int main() { float lowpassed_right = apply_low_pass_filter(&lpf_r, r_in); float preemphasized_left = apply_pre_emphasis(&preemp_l, lowpassed_left); float preemphasized_right = apply_pre_emphasis(&preemp_r, lowpassed_right); - float current_left_input = clip(preemphasized_left); - float current_right_input = clip(preemphasized_right); + float current_left_input = hard_clip(preemphasized_left); + float current_right_input = hard_clip(preemphasized_right); #else float preemphasized_left = apply_pre_emphasis(&preemp_l, l_in); float preemphasized_right = apply_pre_emphasis(&preemp_r, r_in); - float current_left_input = clip(preemphasized_left); - float current_right_input = clip(preemphasized_right); + float current_left_input = hard_clip(preemphasized_left); + float current_right_input = hard_clip(preemphasized_right); #endif #else #ifdef LPF float lowpassed_left = apply_low_pass_filter(&lpf_l, l_in); float lowpassed_right = apply_low_pass_filter(&lpf_r, r_in); - float current_left_input = clip(lowpassed_left); - float current_right_input = clip(lowpassed_right); + float current_left_input = hard_clip(lowpassed_left); + float current_right_input = hard_clip(lowpassed_right); #else - float current_left_input = clip(l_in); - float current_right_input = clip(r_in); + float current_left_input = hard_clip(l_in); + float current_right_input = hard_clip(r_in); #endif #endif float mono = (current_left_input + current_right_input) / 2.0f; // Stereo to Mono float stereo = (current_left_input - current_right_input) / 2.0f; // Also Stereo to Mono but a bit diffrent - - mpx[i] = mono * MONO_VOLUME + + +#ifdef SSB + float stereo_i, stereo_q; + apply_hilbert(&hilbert, stereo, &stereo_i, &stereo_q); // I/Q, the Quadrature data is 90 degrees apart from the In-phase data +#ifdef USB + float signal = (stereo_i*cos38+stereo_q*(sin38*0.775f)); // Compute USB, as the Hilbert isn't perfect, i'll have to a bit silence down the Q carrier in order to make it better, also, it is just perfect as FM Stereo LSB shouldn't be fully LSB +#else + float signal = (stereo_i*cos38-stereo_q*(sin38*0.775f)); // Compute LSB +#endif + mpx[i] = delay_line(&monoDelay, mono) * MONO_VOLUME + pilot * PILOT_VOLUME + - (stereo * stereo_carrier) * STEREO_VOLUME; // DSB-SC modulation + signal*STEREO_VOLUME; +#else + mpx[i] = mono*MONO_VOLUME + + pilot*PILOT_VOLUME + + (stereo*sin38)*STEREO_VOLUME; +#endif } if (pa_simple_write(output_device, mpx, sizeof(mpx), &pulse_error) < 0) { @@ -196,5 +225,9 @@ int main() { printf("Cleaning up...\n"); pa_simple_free(input_device); pa_simple_free(output_device); +#ifdef SSB + exit_hilbert(&hilbert); + exit_delay_line(&monoDelay); +#endif return 0; }