kopia lustrzana https://github.com/sh123/codec2_talkie
Wrapper for FSK modulator/demodulator
rodzic
a297012845
commit
bcd168b6ad
|
@ -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;
|
||||
|
|
|
@ -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
|
|
@ -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
|
|
@ -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);
|
||||
}
|
Ładowanie…
Reference in New Issue