Wrapper for FSK modulator/demodulator

legacy
sh123 2022-07-28 14:46:58 +03:00
rodzic a297012845
commit bcd168b6ad
4 zmienionych plików z 373 dodań i 37 usunięć

Wyświetl plik

@ -2,6 +2,7 @@
#include <cstdlib>
#include "codec2/codec2_fdmdv.h"
#include "codec2/codec2.h"
#include "codec2/fsk.h"
namespace Java_com_ustadmobile_codec2_Codec2 {
@ -14,13 +15,29 @@ namespace Java_com_ustadmobile_codec2_Codec2 {
int nbyte;//size of one frame of codec2 data
};
struct ContextFsk {
struct FSK *fsk;
float *buf;
unsigned char *bits;
COMP * modBuf;
int Nbits;
int N;
};
static Context *getContext(jlong jp) {
unsigned long p = (unsigned long) jp;
auto p = (unsigned long) jp;
Context *con;
con = (Context *) p;
return con;
}
static ContextFsk *getContextFsk(jlong jp) {
auto p = (unsigned long) jp;
ContextFsk *conFsk;
conFsk = (ContextFsk *) p;
return conFsk;
}
static jlong create(JNIEnv *env, jclass clazz, int mode) {
struct Context *con;
con = (struct Context *) malloc(sizeof(struct Context));
@ -32,8 +49,22 @@ namespace Java_com_ustadmobile_codec2_Codec2 {
con->buf = (short*)malloc(con->nsam*sizeof(short));
con->nbyte = (con->nbit + 7) / 8;
con->bits = (unsigned char*)malloc(con->nbyte*sizeof(char));
unsigned long pv = (unsigned long) con;
auto pv = (unsigned long) con;
return pv;
}
static jlong fskCreate(JNIEnv *env, jclass clazz, int sampleFrequency, int symbolRate, int toneFreq, int toneSpacing) {
struct ContextFsk *conFsk;
conFsk = (struct ContextFsk *) malloc(sizeof(struct ContextFsk));
struct FSK *fsk;
fsk = fsk_create(sampleFrequency, symbolRate, MODE_2FSK, toneFreq, toneSpacing);
conFsk->fsk = fsk;
conFsk->Nbits = fsk->Nbits;
conFsk->N = fsk->N;
conFsk->buf = (float*)malloc(conFsk->N);
conFsk->modBuf = (COMP*)malloc(sizeof(COMP)*(fsk->N+fsk->Ts*2));
conFsk->bits = (unsigned char*)malloc(conFsk->Nbits);
auto pv = (unsigned long) conFsk;
return pv;
}
@ -56,20 +87,30 @@ namespace Java_com_ustadmobile_codec2_Codec2 {
return 0;
}
static jlong encode(JNIEnv *env, jclass clazz, jlong n,
jshortArray inputBuffer,
jcharArray outputBits) {
static jint fskDestroy(JNIEnv *env, jclass clazz, jlong n) {
ContextFsk *conFsk = getContextFsk(n);
fsk_destroy(conFsk->fsk);
free(conFsk->bits);
free(conFsk->modBuf);
free(conFsk->buf);
free(conFsk);
return 0;
}
static jlong encode(JNIEnv *env, jclass clazz, jlong n, jshortArray inputBuffer, jcharArray outputBits) {
Context *con = getContext(n);
int i;
jshort *jbuf = env->GetShortArrayElements(inputBuffer, 0);
jshort *jbuf = env->GetShortArrayElements(inputBuffer, nullptr);
for (i = 0; i < con->nsam; i++) {
short v = (short) jbuf[i];
auto v = (short) jbuf[i];
con->buf[i] = v;
}
env->ReleaseShortArrayElements(inputBuffer, jbuf, 0);
//env->DeleteLocalRef(inputBuffer);
codec2_encode(con->c2, con->bits, con->buf);
jchar *jbits = env->GetCharArrayElements(outputBits, 0);
jchar *jbits = env->GetCharArrayElements(outputBits, nullptr);
for (i = 0; i < con->nbyte; i++) {
jbits[i] = con->bits[i];
}
@ -78,26 +119,60 @@ namespace Java_com_ustadmobile_codec2_Codec2 {
return 0;
}
static jlong decode(JNIEnv *env, jclass clazz, jlong n, jshortArray outBuffer, jbyteArray inBuffer) {
static jlong fskModulate(JNIEnv *env, jclass clazz, jlong n, jshortArray outputSamples, jbyteArray inputBits) {
ContextFsk *conFsk = getContextFsk(n);
int i;
jbyte *jbuf = env->GetByteArrayElements(inputBits, nullptr);
for (i = 0; i < conFsk->Nbits; i++) {
auto v = (unsigned char) jbuf[i];
conFsk->bits[i] = v;
}
env->ReleaseByteArrayElements(inputBits, jbuf, 0);
//env->DeleteLocalRef(inputBits);
fsk_mod(conFsk->fsk, conFsk->buf, conFsk->bits, conFsk->Nbits);
jshort *jOutBuf = env->GetShortArrayElements(outputSamples, nullptr);
for (i = 0; i < conFsk->N; i++) {
jOutBuf[i] = (int16_t)(conFsk->buf[i] * FDMDV_SCALE);
}
env->ReleaseShortArrayElements(outputSamples, jOutBuf, 0);
//env->DeleteLocalRef(outputSamples);
return 0;
}
static jlong decode(JNIEnv *env, jclass clazz, jlong n, jshortArray outputSamples, jbyteArray inputBits) {
Context *con = getContext(n);
env->GetByteArrayRegion (inBuffer, 0, con->nbyte, reinterpret_cast<jbyte*>(con->bits));
env->GetByteArrayRegion(inputBits, 0, con->nbyte, reinterpret_cast<jbyte*>(con->bits));
codec2_decode_ber(con->c2, con->buf, con->bits, 0.0);
env->SetShortArrayRegion(outputSamples, 0, con->nsam, con->buf);
return 0;
}
env->SetShortArrayRegion(outBuffer, 0, con->nsam, con->buf);
static jlong fskDemodulate(JNIEnv * env, jclass clazz, jlong n, jshortArray inputSamples, jbyteArray outputBits) {
ContextFsk *conFsk = getContextFsk(n);
env->GetShortArrayRegion(inputSamples, 0, conFsk->N, reinterpret_cast<jshort*>(conFsk->buf));
for(int i = 0; i < fsk_nin(conFsk->fsk); i++){
conFsk->modBuf[i].real = ((float)conFsk->buf[i])/FDMDV_SCALE;
conFsk->modBuf[i].imag = 0.0;
}
fsk_demod(conFsk->fsk, conFsk->bits, conFsk->modBuf);
env->SetByteArrayRegion(outputBits, 0, conFsk->Nbits, reinterpret_cast<const jbyte *>(conFsk->bits));
return 0;
}
static JNINativeMethod method_table[] = {
{"create", "(I)J", (void *) create},
{"getSamplesPerFrame", "(J)I", (void *) c2spf},
{"getBitsSize", "(J)I", (void *) c2bits},
{"destroy", "(J)I", (void *) destroy},
{"encode", "(J[S[C)J", (void *) encode},
{"decode", "(J[S[B)J", (void *) decode}};
{"create", "(I)J", (void *) create},
{"getSamplesPerFrame", "(J)I", (void *) c2spf},
{"getBitsSize", "(J)I", (void *) c2bits},
{"destroy", "(J)I", (void *) destroy},
{"encode", "(J[S[C)J", (void *) encode},
{"decode", "(J[S[B)J", (void *) decode},
{"fskCreate", "(IIII)J", (void *) fskCreate},
{"fskDestroy", "(J)I", (void *) fskDestroy},
{"fskModulate", "(J[S[C)J", (void *) fskModulate},
{"fskDemodulate", "(J[S[S)J", (void *) fskDemodulate}
};
}
using namespace Java_com_ustadmobile_codec2_Codec2;

Wyświetl plik

@ -0,0 +1,222 @@
/*---------------------------------------------------------------------------*\
FILE........: fsk.h
AUTHOR......: Brady O'Brien
DATE CREATED: 6 January 2016
C Implementation of 2FSK/4FSK modulator/demodulator, based on octave/fsk_horus.m
\*---------------------------------------------------------------------------*/
/*
Copyright (C) 2016 David Rowe
All rights reserved.
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU Lesser General Public License version 2.1, as
published by the Free Software Foundation. This program is
distributed in the hope that it will be useful, but WITHOUT ANY
WARRANTY; without even the implied warranty of MERCHANTABILITY or
FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public
License for more details.
You should have received a copy of the GNU Lesser General Public License
along with this program; if not, see <http://www.gnu.org/licenses/>.
*/
#ifdef __cplusplus
extern "C" {
#endif
#ifndef __C2FSK_H
#define __C2FSK_H
#include <stdint.h>
#include "comp.h"
#include "kiss_fftr.h"
#include "modem_stats.h"
#define MODE_2FSK 2
#define MODE_4FSK 4
#define MODE_M_MAX 4
#define FSK_SCALE 16383
/* default internal parameters */
#define FSK_DEFAULT_P 8 /* Number of timing offsets we have to choose from, try to keep P >= 8 */
#define FSK_DEFAULT_NSYM 50 /* See Nsym below */
#define FSK_NONE -1 /* unused parameter */
struct FSK {
/* Static parameters set up by fsk_init */
int Ndft; /* freq offset est fft */
int Fs; /* sample freq */
int N; /* processing buffer size */
int Rs; /* symbol rate */
int Ts; /* samples per symbol */
int Nmem; /* size of extra mem for timing adj */
int P; /* oversample rate for timing est/adj */
int Nsym; /* Number of symbols processed by demodulator in each call, also the timing estimator window */
int Nbits; /* Number of bits spat out in a processing frame */
int f1_tx; /* f1 for modulator */
int tone_spacing; /* Space between TX freqs for modulator (and option mask freq estimator) */
int mode; /* 2FSK or 4FSK */
float tc; /* time constant for smoothing FFTs */
int est_min; /* Minimum frequency for freq. estimator */
int est_max; /* Maximum frequency for freq. estimator */
int est_space; /* Minimum frequency spacing for freq. estimator */
float* hann_table; /* Precomputed or runtime computed hann window table */
/* Parameters used by demod */
float* Sf; /* Average of magnitude spectrum */
COMP phi_c[MODE_M_MAX]; /* phase of each demod local oscillator */
COMP *f_dc; /* down converted samples */
kiss_fft_cfg fft_cfg; /* Config for KISS FFT, used in freq est */
float norm_rx_timing; /* Normalized RX timing */
/* Parameters used by mod */
COMP tx_phase_c; /* TX phase, but complex */
/* Statistics generated by demod */
float EbNodB; /* Estimated EbNo in dB */
float f_est[MODE_M_MAX]; /* Estimated frequencies (peak method) */
float f2_est[MODE_M_MAX];/* Estimated frequencies (mask method) */
int freq_est_type; /* which estimator to use */
float ppm; /* Estimated PPM clock offset */
float SNRest; /* used for LLRs */
float v_est; /* used for LLRs */
/* Parameters used by mod/demod and driving code */
int nin; /* Number of samples to feed the next demod cycle */
int burst_mode; /* enables/disables 'burst' mode */
int lock_nin; /* locks nin during testing */
/* modem statistics struct */
struct MODEM_STATS *stats;
int normalise_eye; /* enables/disables normalisation of eye diagram */
};
/*
* Create a FSK modem
*
* int Fs - Sample frequency
* int Rs - Symbol rate
* int M - 2 for 2FSK, 4 for 4FSK
* int f1_tx - first tone frequency
* int tone_spacing - frequency spacing (for modulator and optional "mask" freq estimator)
*/
struct FSK * fsk_create(int Fs, int Rs, int M, int f1_tx, int tone_spacing);
/*
* Create a FSK modem - advanced version
*
* int Fs - Sample frequency
* int Rs - Symbol rate
* int M - 2 for 2FSK, 4 for 4FSK
* int P - number of timing offsets to choose from (suggest >= 8)
* int Nsym - windows size for timing estimator
* int f1_tx - first tone frequency
* int tone_spacing - frequency spacing (for modulator and optional "mask" freq estimator)
*/
struct FSK * fsk_create_hbr(int Fs, int Rs, int M, int P, int Nsym, int f1_tx, int tone_spacing);
/*
* Set the minimum and maximum frequencies at which the freq. estimator can find tones
*/
void fsk_set_freq_est_limits(struct FSK *fsk,int fmin, int fmax);
/*
* Clear the estimator states
*/
void fsk_clear_estimators(struct FSK *fsk);
/*
* Fills MODEM_STATS struct with demod statistics
*/
void fsk_get_demod_stats(struct FSK *fsk, struct MODEM_STATS *stats);
/*
* Destroy an FSK state struct and free it's memory
*
* struct FSK *fsk - FSK config/state struct to be destroyed
*/
void fsk_destroy(struct FSK *fsk);
/*
* Modulates Nsym bits into N samples
*
* struct FSK *fsk - FSK config/state struct, set up by fsk_create
* float fsk_out[] - Buffer for samples of modulated FSK, fsk->Ts*(Nbits/(M>>1)) in length
* uint8_t tx_bits[] - Buffer containing Nbits unpacked bits
* int Nbits - number of bits to transmit
*/
void fsk_mod(struct FSK *fsk, float fsk_out[], uint8_t tx_bits[], int nbits);
/*
* Modulates Nsym bits into N samples
*
* struct FSK *fsk - FSK config/state struct, set up by fsk_create
* float fsk_out[] - Buffer for samples of "voltage" used to modulate an external VCO
* - fsk->Ts*(Nbits/(M>>1)) in length
* uint8_t tx_bits[] - Buffer containing Nbits unpacked bits
* int Nbits - number of bits to transmit
*/
void fsk_mod_ext_vco(struct FSK *fsk, float vco_out[], uint8_t tx_bits[], int nbits);
/*
* Modulates Nsym bits into N complex samples
*
* struct FSK *fsk - FSK config/state struct, set up by fsk_create
* comp fsk_out[] - Buffer for samples of modulated FSK, fsk->Ts*(Nbits/(M>>1)) in length
* uint8_t tx_bits[] - Buffer containing Nbits unpacked bits
* int Nbits - number of bits to transmit
*/
void fsk_mod_c(struct FSK *fsk, COMP fsk_out[], uint8_t tx_bits[], int nbits);
/*
* Returns the number of samples needed for the next fsk_demod() cycle
*
* struct FSK *fsk - FSK config/state struct, set up by fsk_create
* returns - number of samples to be fed into fsk_demod next cycle
*/
uint32_t fsk_nin(struct FSK *fsk);
/*
* Demodulate some number of FSK samples. The number of samples to be
* demodulated can be found by calling fsk_nin().
*
* struct FSK *fsk - FSK config/state struct, set up by fsk_create
* uint8_t rx_bits[] - Buffer for fsk->Nbits unpacked bits to be written
* float fsk_in[] - nin samples of modulated FSK
*/
void fsk_demod(struct FSK *fsk, uint8_t rx_bits[], COMP fsk_in[]);
/*
* Soft decision demodulation
*
* struct FSK *fsk - FSK config/state struct, set up by fsk_create
* float rx_flit[] - M x Nsym array of filtermagnitude outputs
* float fsk_in[] - nin samples of modualted FSK
*/
void fsk_demod_sd(struct FSK *fsk, float rx_filt[], COMP fsk_in[]);
/* enables/disables normalisation of eye diagram samples */
void fsk_stats_normalise_eye(struct FSK *fsk, int normalise_enable);
/* Set the FSK modem into burst demod mode */
void fsk_enable_burst_mode(struct FSK *fsk);
/* Set freq est algorithm 0: peak 1:mask */
void fsk_set_freq_est_alg(struct FSK *fsk, int est_type);
#endif
#ifdef __cplusplus
}
#endif

Wyświetl plik

@ -0,0 +1,46 @@
#ifndef KISS_FTR_H
#define KISS_FTR_H
#include "kiss_fft.h"
#ifdef __cplusplus
extern "C" {
#endif
/*
Real optimized version can save about 45% cpu time vs. complex fft of a real seq.
*/
typedef struct kiss_fftr_state *kiss_fftr_cfg;
kiss_fftr_cfg kiss_fftr_alloc(int nfft,int inverse_fft,void * mem, size_t * lenmem);
/*
nfft must be even
If you don't care to allocate space, use mem = lenmem = NULL
*/
void kiss_fftr(kiss_fftr_cfg cfg,const kiss_fft_scalar *timedata,kiss_fft_cpx *freqdata);
/*
input timedata has nfft scalar points
output freqdata has nfft/2+1 complex points
*/
void kiss_fftri(kiss_fftr_cfg cfg,const kiss_fft_cpx *freqdata,kiss_fft_scalar *timedata);
/*
input freqdata has nfft/2+1 complex points
output timedata has nfft scalar points
*/
#define kiss_fftr_free free
#ifdef __cplusplus
}
#endif
#endif

Wyświetl plik

@ -27,24 +27,17 @@ public class Codec2 {
public static final int CODEC2_FILE_HEADER_SIZE = 7;
public native static long create(int mode);
public native static int getSamplesPerFrame(long con);
public native static int getBitsSize(long con);
public native static int destroy(long con);
public native static long encode(long con, short[] buf, char[] bits);
public native static int getSamplesPerFrame(long con);
public native static int getBitsSize(long con);
/**
* Decode one frame from codec2.
*
* @param con pointer long, as from the create method
* @param outputBuffer buffer which will be filled with raw PCM audio decoded
* @param bits input buffer containing one frame of audio
*
* @return 0 on successful completion
*/
public native static long decode(long con, short[] outputBuffer, byte[] bits);
public native static long encode(long con, short[] inputSamples, char[] outputBits);
public native static long decode(long con, short[] outputSamples, byte[] inputsBits);
public native static long fskCreate(int sampleFrequency, int symbolRate, int toneFreq, int toneSpacing);
public native static int fskDestroy(long conFsk);
public native static long fskModulate(long conFsk, short[] outputSamples, char[] inputBits);
public native static long fskDemodulate(long conFsk, short[] inputSamples, short[] outputBits);
}