Working PSK31 generator
This commit is contained in:
parent
c23693e885
commit
d3a7c6e12b
4 changed files with 103 additions and 16 deletions
34
README.md
34
README.md
|
@ -185,20 +185,34 @@ Else, after outputing `buf_times` number of buffers (the size of which is stated
|
||||||
|
|
||||||
Along with copying its input samples to the output, it prints a warning message to *stderr* if it finds any IEEE floating point NaN values among the samples.
|
Along with copying its input samples to the output, it prints a warning message to *stderr* if it finds any IEEE floating point NaN values among the samples.
|
||||||
|
|
||||||
floatdump_f
|
dump_f
|
||||||
|
|
||||||
|
It prints all floating point input samples as text.
|
||||||
|
|
||||||
It prints any floating point input samples.
|
|
||||||
The format string used is `"%g "`.
|
The format string used is `"%g "`.
|
||||||
|
|
||||||
|
You can also use it to print complex float values, then you will see the I and Q samples interleaved, like: `I Q I Q I Q ...`
|
||||||
|
|
||||||
|
Alternatively, you can use the `od` command (built into most Linux distributions). To display a list of floating point values with their addresses as well, you can use: `od -vf`
|
||||||
|
|
||||||
|
dump_u8
|
||||||
|
|
||||||
|
It prints all input bytes as text, in hexadecimal form.
|
||||||
|
|
||||||
|
Alternatively, you can use the `xxd` command (built into most Linux distributions). To display a hexadecimal dump of the standard input (with addresses at the beginning of rows), you can use: `xxd -g1`
|
||||||
|
|
||||||
flowcontrol <data_rate> <reads_per_second>
|
flowcontrol <data_rate> <reads_per_second>
|
||||||
|
|
||||||
It limits the data rate of a stream to a given `data_rate` number of bytes per second.
|
It limits the data rate of a stream to a given `data_rate` number of bytes per second.
|
||||||
|
|
||||||
It copies `data_rate / reads_per_second` bytes from the input to the output, doing it `reads_per_second` times every second.
|
It copies `data_rate / reads_per_second` bytes from the input to the output, doing it `reads_per_second` times every second.
|
||||||
|
|
||||||
shift_math_cc <rate>
|
shift_math_cc <rate>
|
||||||
|
|
||||||
It shifts the signal in the frequency domain by `rate`.
|
It shifts the signal in the frequency domain by `rate`.
|
||||||
|
|
||||||
`rate` is a floating point number between -0.5 and 0.5.
|
`rate` is a floating point number between -0.5 and 0.5.
|
||||||
|
|
||||||
`rate` is relative to the sampling rate.
|
`rate` is relative to the sampling rate.
|
||||||
|
|
||||||
Internally, a sine and cosine wave is generated to perform this function, and this function uses `math.h` for this purpose, which is quite accurate, but not always very fast.
|
Internally, a sine and cosine wave is generated to perform this function, and this function uses `math.h` for this purpose, which is quite accurate, but not always very fast.
|
||||||
|
@ -417,6 +431,22 @@ This is a controllable squelch, which reads the squelch level input from `<squel
|
||||||
|
|
||||||
It is similar to `clone`, but internally it uses a circular buffer. It reads as much as possible from the input. It discards input samples if the input buffer is full.
|
It is similar to `clone`, but internally it uses a circular buffer. It reads as much as possible from the input. It discards input samples if the input buffer is full.
|
||||||
|
|
||||||
|
psk31_varicode_encoder_u8_u8
|
||||||
|
|
||||||
|
It encodes ASCII characters into varicode for PSK31 transmission. It puts a `00` sequence between the varicode characters (which acts as a separator).
|
||||||
|
|
||||||
|
For the Varicode character set, see: http://www.arrl.org/psk31-spec
|
||||||
|
|
||||||
|
For example, `aaa` means the bit sequence `101100101100101100`.
|
||||||
|
|
||||||
|
For this input, the output of `psk31_varicode_encoder_u8_u8` will be the following bytes (in hexadecimal):
|
||||||
|
|
||||||
|
```
|
||||||
|
01 00 01 01 00 00 01 00
|
||||||
|
01 01 00 00 01 00 01 01
|
||||||
|
00 00
|
||||||
|
```
|
||||||
|
|
||||||
#### Control via pipes
|
#### Control via pipes
|
||||||
|
|
||||||
Some parameters can be changed while the `csdr` process is running. To achieve this, some `csdr` functions have special parameters. You have to supply a fifo previously created by the `mkfifo` command. Processing will only start after the first control command has been received by `csdr` over the FIFO.
|
Some parameters can be changed while the `csdr` process is running. To achieve this, some `csdr` functions have special parameters. You have to supply a fifo previously created by the `mkfifo` command. Processing will only start after the first control command has been received by `csdr` over the FIFO.
|
||||||
|
|
41
csdr.c
Normal file → Executable file
41
csdr.c
Normal file → Executable file
|
@ -73,7 +73,7 @@ char usage[]=
|
||||||
" none\n"
|
" none\n"
|
||||||
" yes_f <to_repeat> [buf_times]\n"
|
" yes_f <to_repeat> [buf_times]\n"
|
||||||
" detect_nan_ff\n"
|
" detect_nan_ff\n"
|
||||||
" floatdump_f\n"
|
" dump_f\n"
|
||||||
" flowcontrol <data_rate> <reads_per_second> [prebuffer_sec] [thrust]\n"
|
" flowcontrol <data_rate> <reads_per_second> [prebuffer_sec] [thrust]\n"
|
||||||
" shift_math_cc <rate>\n"
|
" shift_math_cc <rate>\n"
|
||||||
" shift_math_cc --fifo <fifo_path>\n"
|
" shift_math_cc --fifo <fifo_path>\n"
|
||||||
|
@ -124,6 +124,14 @@ char usage[]=
|
||||||
" serial_line_decoder_sy_u8\n"
|
" serial_line_decoder_sy_u8\n"
|
||||||
" octave_complex_c <samples_to_plot> <out_of_n_samples>\n"
|
" octave_complex_c <samples_to_plot> <out_of_n_samples>\n"
|
||||||
" timing_recovery_cc <algorithm> <decimation> [--add_q]\n"
|
" timing_recovery_cc <algorithm> <decimation> [--add_q]\n"
|
||||||
|
" psk31_varicode_encoder_u8_u8\n"
|
||||||
|
" differential_encoder_u8_u8\n"
|
||||||
|
" differential_decoder_u8_u8\n"
|
||||||
|
" dump_u8\n"
|
||||||
|
" psk_modulator_u8_c <n_psk>\n"
|
||||||
|
" psk31_interpolate_sine_cc\n"
|
||||||
|
" duplicate_samples_ntimes_u8_u8 <sample_size_bytes> <ntimes>\n"
|
||||||
|
" ?<search_the_function_list>\n"
|
||||||
" \n"
|
" \n"
|
||||||
;
|
;
|
||||||
|
|
||||||
|
@ -2405,6 +2413,8 @@ int main(int argc, char *argv[])
|
||||||
int out_of_n_samples = 0;
|
int out_of_n_samples = 0;
|
||||||
sscanf(argv[3], "%d", &out_of_n_samples);
|
sscanf(argv[3], "%d", &out_of_n_samples);
|
||||||
if(out_of_n_samples<samples_to_plot) badsyntax("out_of_n_samples should be < samples_to_plot");
|
if(out_of_n_samples<samples_to_plot) badsyntax("out_of_n_samples should be < samples_to_plot");
|
||||||
|
int mode2d = 0;
|
||||||
|
if(argc>4) mode2d = !strcmp(argv[4], "--2d");
|
||||||
complexf* read_buf = (complexf*)malloc(sizeof(complexf)*the_bufsize);
|
complexf* read_buf = (complexf*)malloc(sizeof(complexf)*the_bufsize);
|
||||||
|
|
||||||
if(!sendbufsize(initialize_buffers())) return -2;
|
if(!sendbufsize(initialize_buffers())) return -2;
|
||||||
|
@ -2415,7 +2425,9 @@ int main(int argc, char *argv[])
|
||||||
for(int i=0;i<samples_to_plot;i++) printf("%f ", iof(read_buf, i));
|
for(int i=0;i<samples_to_plot;i++) printf("%f ", iof(read_buf, i));
|
||||||
printf("];\nqsig = [");
|
printf("];\nqsig = [");
|
||||||
for(int i=0;i<samples_to_plot;i++) printf("%f ", qof(read_buf, i));
|
for(int i=0;i<samples_to_plot;i++) printf("%f ", qof(read_buf, i));
|
||||||
printf("];\nzsig = [0:N-1];\nplot3(isig,zsig,qsig);\n");
|
printf("];\nzsig = [0:N-1];\n");
|
||||||
|
if(mode2d) printf("subplot(2,1,1);\nplot(zsig,isig);\nsubplot(2,1,2);\nplot(zsig,qsig);\n");
|
||||||
|
else printf("plot3(isig,zsig,qsig);\n");
|
||||||
//printf("xlim([-1 1]);\nzlim([-1 1]);\n");
|
//printf("xlim([-1 1]);\nzlim([-1 1]);\n");
|
||||||
fflush(stdout);
|
fflush(stdout);
|
||||||
//if(fseek(stdin, (out_of_n_samples - samples_to_plot)*sizeof(complexf), SEEK_CUR)<0) { perror("fseek error"); return -3; } //this cannot be used on stdin
|
//if(fseek(stdin, (out_of_n_samples - samples_to_plot)*sizeof(complexf), SEEK_CUR)<0) { perror("fseek error"); return -3; } //this cannot be used on stdin
|
||||||
|
@ -2450,11 +2462,13 @@ int main(int argc, char *argv[])
|
||||||
|
|
||||||
if(!strcmp(argv[1],"duplicate_samples_ntimes_u8_u8")) //<sample_size_bytes> <ntimes>
|
if(!strcmp(argv[1],"duplicate_samples_ntimes_u8_u8")) //<sample_size_bytes> <ntimes>
|
||||||
{
|
{
|
||||||
int sample_size_bytes, ntimes;
|
int sample_size_bytes = 0, ntimes = 0;
|
||||||
if(argc<=2) return badsyntax("need required parameter (sample_size_bytes)");
|
if(argc<=2) return badsyntax("need required parameter (sample_size_bytes)");
|
||||||
sscanf(argv[2],"%d",&sample_size_bytes);
|
sscanf(argv[2],"%d",&sample_size_bytes);
|
||||||
|
if(sample_size_bytes<=0) badsyntax("sample_size_bytes should be >0");
|
||||||
if(argc<=3) return badsyntax("need required parameter (ntimes)");
|
if(argc<=3) return badsyntax("need required parameter (ntimes)");
|
||||||
sscanf(argv[3],"%d",&ntimes);
|
sscanf(argv[3],"%d",&ntimes);
|
||||||
|
if(ntimes<=0) badsyntax("ntimes should be >0");
|
||||||
if(!initialize_buffers()) return -2;
|
if(!initialize_buffers()) return -2;
|
||||||
sendbufsize(the_bufsize*ntimes);
|
sendbufsize(the_bufsize*ntimes);
|
||||||
unsigned char* local_input_buffer = (unsigned char*)malloc(sizeof(unsigned char)*the_bufsize*sample_size_bytes);
|
unsigned char* local_input_buffer = (unsigned char*)malloc(sizeof(unsigned char)*the_bufsize*sample_size_bytes);
|
||||||
|
@ -2463,7 +2477,7 @@ int main(int argc, char *argv[])
|
||||||
{
|
{
|
||||||
FEOF_CHECK;
|
FEOF_CHECK;
|
||||||
fread((void*)local_input_buffer, sizeof(unsigned char), the_bufsize*sample_size_bytes, stdin);
|
fread((void*)local_input_buffer, sizeof(unsigned char), the_bufsize*sample_size_bytes, stdin);
|
||||||
duplicate_samples_ntimes_u8_u8(local_input_buffer, local_input_buffer, the_bufsize*sample_size_bytes, sample_size_bytes, ntimes);
|
duplicate_samples_ntimes_u8_u8(local_input_buffer, local_output_buffer, the_bufsize*sample_size_bytes, sample_size_bytes, ntimes);
|
||||||
fwrite((void*)local_output_buffer, sizeof(unsigned char), the_bufsize*sample_size_bytes*ntimes, stdout);
|
fwrite((void*)local_output_buffer, sizeof(unsigned char), the_bufsize*sample_size_bytes*ntimes, stdout);
|
||||||
TRY_YIELD;
|
TRY_YIELD;
|
||||||
}
|
}
|
||||||
|
@ -2520,6 +2534,7 @@ int main(int argc, char *argv[])
|
||||||
for(;;)
|
for(;;)
|
||||||
{
|
{
|
||||||
psk31_varicode_encoder_u8_u8(local_input_buffer, local_output_buffer, the_bufsize, output_max_size, &input_processed, &output_size);
|
psk31_varicode_encoder_u8_u8(local_input_buffer, local_output_buffer, the_bufsize, output_max_size, &input_processed, &output_size);
|
||||||
|
//fprintf(stderr, "os = %d\n", output_size);
|
||||||
fwrite((void*)local_output_buffer, sizeof(unsigned char), output_size, stdout);
|
fwrite((void*)local_output_buffer, sizeof(unsigned char), output_size, stdout);
|
||||||
FEOF_CHECK;
|
FEOF_CHECK;
|
||||||
memmove(local_input_buffer, local_input_buffer+input_processed, the_bufsize-input_processed);
|
memmove(local_input_buffer, local_input_buffer+input_processed, the_bufsize-input_processed);
|
||||||
|
@ -2542,6 +2557,24 @@ int main(int argc, char *argv[])
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
int differential_codec_encode = 0;
|
||||||
|
if( (differential_codec_encode = !strcmp(argv[1],"differential_encoder_u8_u8")) || (!strcmp(argv[1],"differential_decoder_u8_u8")) )
|
||||||
|
{
|
||||||
|
if(!initialize_buffers()) return -2;
|
||||||
|
sendbufsize(the_bufsize);
|
||||||
|
unsigned char* local_input_buffer = (unsigned char*)malloc(sizeof(unsigned char)*the_bufsize);
|
||||||
|
unsigned char* local_output_buffer = (unsigned char*)malloc(sizeof(unsigned char)*the_bufsize);
|
||||||
|
unsigned char state = 0;
|
||||||
|
for(;;)
|
||||||
|
{
|
||||||
|
FEOF_CHECK;
|
||||||
|
fread((void*)local_input_buffer, sizeof(unsigned char), the_bufsize, stdin);
|
||||||
|
state = differential_codec(local_input_buffer, local_output_buffer, the_bufsize, differential_codec_encode, state);
|
||||||
|
fwrite((void*)local_output_buffer, sizeof(unsigned char), the_bufsize, stdout);
|
||||||
|
TRY_YIELD;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
if(!strcmp(argv[1],"none"))
|
if(!strcmp(argv[1],"none"))
|
||||||
{
|
{
|
||||||
return 0;
|
return 0;
|
||||||
|
|
43
libcsdr.c
43
libcsdr.c
|
@ -1415,19 +1415,22 @@ char psk31_varicode_decoder_push(unsigned long long* status_shr, unsigned char s
|
||||||
|
|
||||||
void psk31_varicode_encoder_u8_u8(unsigned char* input, unsigned char* output, int input_size, int output_max_size, int* input_processed, int* output_size)
|
void psk31_varicode_encoder_u8_u8(unsigned char* input, unsigned char* output, int input_size, int output_max_size, int* input_processed, int* output_size)
|
||||||
{
|
{
|
||||||
*output_size=0;
|
(*output_size)=0;
|
||||||
for(*input_processed=0; *input_processed<input_size; *input_processed++)
|
for((*input_processed)=0; (*input_processed)<input_size; (*input_processed)++)
|
||||||
{
|
{
|
||||||
|
//fprintf(stderr, "ii = %d, input_size = %d, output_max_size = %d\n", *input_processed, input_size, output_max_size);
|
||||||
for(int ci=0; ci<n_psk31_varicode_items; ci++) //ci: character index
|
for(int ci=0; ci<n_psk31_varicode_items; ci++) //ci: character index
|
||||||
{
|
{
|
||||||
psk31_varicode_item_t current_varicode = psk31_varicode_items[ci];
|
psk31_varicode_item_t current_varicode = psk31_varicode_items[ci];
|
||||||
if(input[*input_processed]==current_varicode.ascii)
|
if(input[*input_processed]==current_varicode.ascii)
|
||||||
{
|
{
|
||||||
if(output_max_size<current_varicode.bitcount) return;
|
//fprintf(stderr, "ci = %d\n", ci);
|
||||||
for(int bi=0; bi<current_varicode.bitcount; bi++) //bi: bit index
|
if(output_max_size<current_varicode.bitcount+2) return;
|
||||||
|
for(int bi=0; bi<current_varicode.bitcount+2; bi++) //bi: bit index
|
||||||
{
|
{
|
||||||
output[*output_size]=(psk31_varicode_items[ci].code>>(current_varicode.bitcount-bi-1))&1;
|
//fprintf(stderr, "bi = %d\n", bi);
|
||||||
*output_size++;
|
output[*output_size] = (bi<current_varicode.bitcount) ? (psk31_varicode_items[ci].code>>(current_varicode.bitcount-bi-1))&1 : 0;
|
||||||
|
(*output_size)++;
|
||||||
output_max_size--;
|
output_max_size--;
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
|
@ -1598,9 +1601,10 @@ void binary_slicer_f_u8(float* input, unsigned char* output, int input_size)
|
||||||
void psk_modulator_u8_c(unsigned char* input, complexf* output, int input_size, int n_psk)
|
void psk_modulator_u8_c(unsigned char* input, complexf* output, int input_size, int n_psk)
|
||||||
{
|
{
|
||||||
//outputs one complex sample per input symbol
|
//outputs one complex sample per input symbol
|
||||||
|
float phase_increment = (2*M_PI)/n_psk;
|
||||||
for(int i=0;i<input_size;i++)
|
for(int i=0;i<input_size;i++)
|
||||||
{
|
{
|
||||||
float out_phase=((2*M_PI)/n_psk)*input[i];
|
float out_phase=phase_increment*input[i];
|
||||||
iof(output,i)=cos(out_phase);
|
iof(output,i)=cos(out_phase);
|
||||||
qof(output,i)=sin(out_phase);
|
qof(output,i)=sin(out_phase);
|
||||||
}
|
}
|
||||||
|
@ -1617,14 +1621,18 @@ void duplicate_samples_ntimes_u8_u8(unsigned char* input, unsigned char* output,
|
||||||
|
|
||||||
complexf psk31_interpolate_sine_cc(complexf* input, complexf* output, int input_size, int interpolation, complexf last_input)
|
complexf psk31_interpolate_sine_cc(complexf* input, complexf* output, int input_size, int interpolation, complexf last_input)
|
||||||
{
|
{
|
||||||
|
int oi=0; //output index
|
||||||
for(int i=0;i<input_size;i++)
|
for(int i=0;i<input_size;i++)
|
||||||
|
{
|
||||||
for(int j=0; j<interpolation; j++)
|
for(int j=0; j<interpolation; j++)
|
||||||
{
|
{
|
||||||
float rate = (1+sin(-(M_PI/2)+M_PI*((j+1)/(float)interpolation)))/2;
|
float rate = (1+sin(-(M_PI/2)+M_PI*((j+1)/(float)interpolation)))/2;
|
||||||
iof(output,i)=iof(input,i) * rate + iof(&last_input,0) * (1-rate);
|
iof(output,oi)=iof(input,i) * rate + iof(&last_input,0) * (1-rate);
|
||||||
qof(output,i)=qof(input,i) * rate + qof(&last_input,0) * (1-rate);
|
qof(output,oi)=qof(input,i) * rate + qof(&last_input,0) * (1-rate);
|
||||||
last_input = output[i];
|
oi++;
|
||||||
}
|
}
|
||||||
|
last_input = input[i];
|
||||||
|
}
|
||||||
return last_input;
|
return last_input;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1635,6 +1643,21 @@ void pack_bits_8to1_u8_u8(unsigned char* input, unsigned char* output, int input
|
||||||
*(output++)=(input[i]>>bi)&1;
|
*(output++)=(input[i]>>bi)&1;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
unsigned char differential_codec(unsigned char* input, unsigned char* output, int input_size, int encode, unsigned char state)
|
||||||
|
{
|
||||||
|
if(!encode)
|
||||||
|
for(int i=0;i<input_size;i++)
|
||||||
|
{
|
||||||
|
output[i] = input[i] == state;
|
||||||
|
state = input[i];
|
||||||
|
}
|
||||||
|
else
|
||||||
|
for(int i=0;i<input_size;i++)
|
||||||
|
{
|
||||||
|
if(!input[i]) state=!state;
|
||||||
|
output[i] = state;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
_____ _ _______ _ _ _____
|
_____ _ _______ _ _ _____
|
||||||
|
|
|
@ -322,3 +322,4 @@ void duplicate_samples_ntimes_u8_u8(unsigned char* input, unsigned char* output,
|
||||||
complexf psk31_interpolate_sine_cc(complexf* input, complexf* output, int input_size, int interpolation, complexf last_input);
|
complexf psk31_interpolate_sine_cc(complexf* input, complexf* output, int input_size, int interpolation, complexf last_input);
|
||||||
void pack_bits_8to1_u8_u8(unsigned char* input, unsigned char* output, int input_size);
|
void pack_bits_8to1_u8_u8(unsigned char* input, unsigned char* output, int input_size);
|
||||||
void psk31_varicode_encoder_u8_u8(unsigned char* input, unsigned char* output, int input_size, int output_max_size, int* input_processed, int* output_size);
|
void psk31_varicode_encoder_u8_u8(unsigned char* input, unsigned char* output, int input_size, int output_max_size, int* input_processed, int* output_size);
|
||||||
|
unsigned char differential_codec(unsigned char* input, unsigned char* output, int input_size, int encode, unsigned char state);
|
||||||
|
|
Loading…
Reference in a new issue