Initial commit

This commit is contained in:
2024-11-03 18:57:02 -06:00
commit a847840202
149 changed files with 143300 additions and 0 deletions

8
.gitignore vendored Normal file
View File

@@ -0,0 +1,8 @@
.DS_Store
/.build
xcuserdata/
DerivedData/
.swiftpm/configuration/registries.json
.swiftpm/xcode/package.xcworkspace/contents.xcworkspacedata
.netrc
*.class

42
Package.resolved Normal file
View File

@@ -0,0 +1,42 @@
{
"originHash" : "8b424c89cf909d23f0294225dd14ece13680ef4d122b099e92d093e9817a9b21",
"pins" : [
{
"identity" : "swift-algorithms",
"kind" : "remoteSourceControl",
"location" : "https://github.com/apple/swift-algorithms",
"state" : {
"revision" : "f6919dfc309e7f1b56224378b11e28bab5bccc42",
"version" : "1.2.0"
}
},
{
"identity" : "swift-numerics",
"kind" : "remoteSourceControl",
"location" : "https://github.com/apple/swift-numerics.git",
"state" : {
"revision" : "0a5bc04095a675662cf24757cc0640aa2204253b",
"version" : "1.0.2"
}
},
{
"identity" : "swift-odeint",
"kind" : "remoteSourceControl",
"location" : "https://github.com/jkl1337/swift-odeint",
"state" : {
"branch" : "master",
"revision" : "6e195efd2276dec7cedd2745be8ed61e0cbaa370"
}
},
{
"identity" : "swiftpffft",
"kind" : "remoteSourceControl",
"location" : "https://github.com/jkl1337/SwiftPFFFT",
"state" : {
"branch" : "master",
"revision" : "437f984b112fecbd4303d21f7c934f2085b6d9af"
}
}
],
"version" : 3
}

34
Package.swift Normal file
View File

@@ -0,0 +1,34 @@
// swift-tools-version: 6.0
// The swift-tools-version declares the minimum version of Swift required to build this package.
import PackageDescription
let package = Package(
name: "ECGSynKit",
products: [
// Products define the executables and libraries a package produces, making them visible to other packages.
.library(
name: "ECGSynKit",
targets: ["ECGSynKit"]
),
],
dependencies: [
.package(url: "https://github.com/apple/swift-algorithms", from: "1.2.0"),
.package(url: "https://github.com/jkl1337/SwiftPFFFT", branch: "master"),
.package(url: "https://github.com/jkl1337/swift-odeint", branch: "master"),
],
targets: [
.target(
name: "ECGSynKit",
dependencies: [
.product(name: "PFFFT", package: "SwiftPFFFT"),
.product(name: "Algorithms", package: "swift-algorithms"),
.product(name: "OdeInt", package: "swift-odeint"),
]
),
.testTarget(
name: "ECGSynKitTests",
dependencies: ["ECGSynKit"]
),
]
)

7
Packages/KissFFT/.gitignore vendored Normal file
View File

@@ -0,0 +1,7 @@
.DS_Store
/.build
/Packages
/*.xcodeproj
xcuserdata/
DerivedData/
.swiftpm/xcode/package.xcworkspace/contents.xcworkspacedata

11
Packages/KissFFT/COPYING Normal file
View File

@@ -0,0 +1,11 @@
Copyright (c) 2003-2010 Mark Borgerding
All rights reserved.
Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met:
* Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer.
* Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution.
* Neither the author nor the names of any contributors may be used to endorse or promote products derived from this software without specific prior written permission.
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.

View File

@@ -0,0 +1,10 @@
// swift-tools-version:5.3
// The swift-tools-version declares the minimum version of Swift required to build this package.
import PackageDescription
let package = Package(
name: "KissFFT",
products: [ .library(name: "KissFFT", targets: ["KissFFT"])],
targets: [.target(name: "KissFFT", publicHeadersPath: "include")]
)

145
Packages/KissFFT/README.md Normal file
View File

@@ -0,0 +1,145 @@
# Kiss FFT
Swift Package implementation of Kiss FFT, described below.
## A mixed-radix Fast Fourier Transform based up on the principle, "Keep It Simple, Stupid."
There are many great fft libraries already around. Kiss FFT is not trying
to be better than any of them. It only attempts to be a reasonably efficient,
moderately useful FFT that can use fixed or floating data types and can be
incorporated into someone's C program in a few minutes with trivial licensing.
## USAGE:
The basic usage for 1-d complex FFT is:
#include "kiss_fft.h"
kiss_fft_cfg cfg = kiss_fft_alloc( nfft ,is_inverse_fft ,0,0 );
while ...
... // put kth sample in cx_in[k].r and cx_in[k].i
kiss_fft( cfg , cx_in , cx_out );
... // transformed. DC is in cx_out[0].r and cx_out[0].i
free(cfg);
Note: frequency-domain data is stored from dc up to 2pi.
so cx_out[0] is the dc bin of the FFT
and cx_out[nfft/2] is the Nyquist bin (if exists)
Declarations are in "kiss_fft.h", along with a brief description of the
functions you'll need to use.
Code definitions for 1d complex FFTs are in kiss_fft.c.
You can do other cool stuff with the extras you'll find in tools/
* multi-dimensional FFTs
* real-optimized FFTs (returns the positive half-spectrum: (nfft/2+1) complex frequency bins)
* fast convolution FIR filtering (not available for fixed point)
* spectrum image creation
The core fft and most tools/ code can be compiled to use float, double
or Q15 short samples. The default is float.
## BACKGROUND:
I started coding this because I couldn't find a fixed point FFT that didn't
use assembly code. I started with floating point numbers so I could get the
theory straight before working on fixed point issues. In the end, I had a
little bit of code that could be recompiled easily to do ffts with short, float
or double (other types should be easy too).
Once I got my FFT working, I was curious about the speed compared to
a well respected and highly optimized fft library. I don't want to criticize
this great library, so let's call it FFT_BRANDX.
During this process, I learned:
1. FFT_BRANDX has more than 100K lines of code. The core of kiss_fft is about 500 lines (cpx 1-d).
2. It took me an embarrassingly long time to get FFT_BRANDX working.
3. A simple program using FFT_BRANDX is 522KB. A similar program using kiss_fft is 18KB (without optimizing for size).
4. FFT_BRANDX is roughly twice as fast as KISS FFT in default mode.
It is wonderful that free, highly optimized libraries like FFT_BRANDX exist.
But such libraries carry a huge burden of complexity necessary to extract every
last bit of performance.
Sometimes simpler is better, even if it's not better.
## FREQUENTLY ASKED QUESTIONS:
Q: Can I use kissfft in a project with a ___ license?
A: Yes. See LICENSE below.
Q: Why don't I get the output I expect?
A: The two most common causes of this are
1) scaling : is there a constant multiplier between what you got and what you want?
2) mixed build environment -- all code must be compiled with same preprocessor
definitions for FIXED_POINT and kiss_fft_scalar
Q: Will you write/debug my code for me?
A: Probably not unless you pay me. I am happy to answer pointed and topical questions, but
I may refer you to a book, a forum, or some other resource.
## PERFORMANCE: (on Athlon XP 2100+, with gcc 2.96, float data type)
Kiss performed 10000 1024-pt cpx ffts in .63 s of cpu time.
For comparison, it took md5sum twice as long to process the same amount of data.
Transforming 5 minutes of CD quality audio takes less than a second (nfft=1024).
DO NOT:
... use Kiss if you need the Fastest Fourier Transform in the World
... ask me to add features that will bloat the code
## UNDER THE HOOD:
Kiss FFT uses a time decimation, mixed-radix, out-of-place FFT. If you give it an input buffer
and output buffer that are the same, a temporary buffer will be created to hold the data.
No static data is used. The core routines of kiss_fft are thread-safe (but not all of the tools directory).
No scaling is done for the floating point version (for speed).
Scaling is done both ways for the fixed-point version (for overflow prevention).
Optimized butterflies are used for factors 2,3,4, and 5.
The real (i.e. not complex) optimization code only works for even length ffts. It does two half-length
FFTs in parallel (packed into real&imag), and then combines them via twiddling. The result is
nfft/2+1 complex frequency bins from DC to Nyquist. If you don't know what this means, search the web.
The fast convolution filtering uses the overlap-scrap method, slightly
modified to put the scrap at the tail.
## LICENSE:
Revised BSD License, see COPYING for verbiage.
Basically, "free to use&change, give credit where due, no guarantees"
Note this license is compatible with GPL at one end of the spectrum and closed, commercial software at
the other end. See http://www.fsf.org/licensing/licenses
A commercial license is available which removes the requirement for attribution. Contact me for details.
## FUTURE:
* Add real optimization for odd length FFTs
* Document/revisit the input/output fft scaling
* Make doc describing the overlap (tail) scrap fast convolution filtering in kiss_fastfir.c
* Test all the ./tools/ code with fixed point (kiss_fastfir.c doesn't work, maybe others)
## AUTHOR
Mark Borgerding
Mark@Borgerding.net

View File

@@ -0,0 +1,168 @@
/*
Copyright (c) 2003-2010, Mark Borgerding
All rights reserved.
Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met:
* Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer.
* Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution.
* Neither the author nor the names of any contributors may be used to endorse or promote products derived from this software without specific prior written permission.
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
/* kiss_fft.h
defines kiss_fft_scalar as either short or a float type
and defines
typedef struct { kiss_fft_scalar r; kiss_fft_scalar i; }kiss_fft_cpx; */
#include "kiss_fft.h"
#include <limits.h>
#define MAXFACTORS 32
/* e.g. an fft of length 128 has 4 factors
as far as kissfft is concerned
4*4*4*2
*/
#ifndef SOUNDPIPE_H
struct kiss_fft_state{
int nfft;
int inverse;
int factors[2*MAXFACTORS];
kiss_fft_cpx twiddles[1];
};
#endif
/*
Explanation of macros dealing with complex math:
C_MUL(m,a,b) : m = a*b
C_FIXDIV( c , div ) : if a fixed point impl., c /= div. noop otherwise
C_SUB( res, a,b) : res = a - b
C_SUBFROM( res , a) : res -= a
C_ADDTO( res , a) : res += a
* */
#ifdef FIXED_POINT
#if (FIXED_POINT==32)
# define FRACBITS 31
# define SAMPPROD int64_t
#define SAMP_MAX 2147483647
#else
# define FRACBITS 15
# define SAMPPROD int32_t
#define SAMP_MAX 32767
#endif
#define SAMP_MIN -SAMP_MAX
#if defined(CHECK_OVERFLOW)
# define CHECK_OVERFLOW_OP(a,op,b) \
if ( (SAMPPROD)(a) op (SAMPPROD)(b) > SAMP_MAX || (SAMPPROD)(a) op (SAMPPROD)(b) < SAMP_MIN ) { \
fprintf(stderr,"WARNING:overflow @ " __FILE__ "(%d): (%d " #op" %d) = %ld\n",__LINE__,(a),(b),(SAMPPROD)(a) op (SAMPPROD)(b) ); }
#endif
# define smul(a,b) ( (SAMPPROD)(a)*(b) )
# define sround( x ) (kiss_fft_scalar)( ( (x) + (1<<(FRACBITS-1)) ) >> FRACBITS )
# define S_MUL(a,b) sround( smul(a,b) )
# define C_MUL(m,a,b) \
do{ (m).r = sround( smul((a).r,(b).r) - smul((a).i,(b).i) ); \
(m).i = sround( smul((a).r,(b).i) + smul((a).i,(b).r) ); }while(0)
# define DIVSCALAR(x,k) \
(x) = sround( smul( x, SAMP_MAX/k ) )
# define C_FIXDIV(c,div) \
do { DIVSCALAR( (c).r , div); \
DIVSCALAR( (c).i , div); }while (0)
# define C_MULBYSCALAR( c, s ) \
do{ (c).r = sround( smul( (c).r , s ) ) ;\
(c).i = sround( smul( (c).i , s ) ) ; }while(0)
#else /* not FIXED_POINT*/
# define S_MUL(a,b) ( (a)*(b) )
#define C_MUL(m,a,b) \
do{ (m).r = (a).r*(b).r - (a).i*(b).i;\
(m).i = (a).r*(b).i + (a).i*(b).r; }while(0)
# define C_FIXDIV(c,div) /* NOOP */
# define C_MULBYSCALAR( c, s ) \
do{ (c).r *= (s);\
(c).i *= (s); }while(0)
#endif
#ifndef CHECK_OVERFLOW_OP
# define CHECK_OVERFLOW_OP(a,op,b) /* noop */
#endif
#define C_ADD( res, a,b)\
do { \
CHECK_OVERFLOW_OP((a).r,+,(b).r)\
CHECK_OVERFLOW_OP((a).i,+,(b).i)\
(res).r=(a).r+(b).r; (res).i=(a).i+(b).i; \
}while(0)
#define C_SUB( res, a,b)\
do { \
CHECK_OVERFLOW_OP((a).r,-,(b).r)\
CHECK_OVERFLOW_OP((a).i,-,(b).i)\
(res).r=(a).r-(b).r; (res).i=(a).i-(b).i; \
}while(0)
#define C_ADDTO( res , a)\
do { \
CHECK_OVERFLOW_OP((res).r,+,(a).r)\
CHECK_OVERFLOW_OP((res).i,+,(a).i)\
(res).r += (a).r; (res).i += (a).i;\
}while(0)
#define C_SUBFROM( res , a)\
do {\
CHECK_OVERFLOW_OP((res).r,-,(a).r)\
CHECK_OVERFLOW_OP((res).i,-,(a).i)\
(res).r -= (a).r; (res).i -= (a).i; \
}while(0)
#ifdef FIXED_POINT
# define KISS_FFT_COS(phase) floor(.5+SAMP_MAX * cos (phase))
# define KISS_FFT_SIN(phase) floor(.5+SAMP_MAX * sin (phase))
# define HALF_OF(x) ((x)>>1)
#elif defined(USE_SIMD)
# define KISS_FFT_COS(phase) _mm_set1_ps( cos(phase) )
# define KISS_FFT_SIN(phase) _mm_set1_ps( sin(phase) )
# define HALF_OF(x) ((x)*_mm_set1_ps(.5))
#else
# define KISS_FFT_COS(phase) (kiss_fft_scalar) cos(phase)
# define KISS_FFT_SIN(phase) (kiss_fft_scalar) sin(phase)
# define HALF_OF(x) ((x)*.5)
#endif
#define kf_cexp(x,phase) \
do{ \
(x)->r = KISS_FFT_COS(phase);\
(x)->i = KISS_FFT_SIN(phase);\
}while(0)
/* a debugging function */
#define pcpx(c)\
fprintf(stderr,"%g + %gi\n",(double)((c)->r),(double)((c)->i) )
#ifdef KISS_FFT_USE_ALLOCA
// define this to allow use of alloca instead of malloc for temporary buffers
// Temporary buffers are used in two case:
// 1. FFT sizes that have "bad" factors. i.e. not 2,3 and 5
// 2. "in-place" FFTs. Notice the quotes, since kissfft does not really do an in-place transform.
#include <alloca.h>
#define KISS_FFT_TMP_ALLOC(nbytes) alloca(nbytes)
#define KISS_FFT_TMP_FREE(ptr)
#else
#define KISS_FFT_TMP_ALLOC(nbytes) KISS_FFT_MALLOC(nbytes)
#define KISS_FFT_TMP_FREE(ptr) KISS_FFT_FREE(ptr)
#endif

View File

@@ -0,0 +1,128 @@
#ifndef KISS_FFT_H
#define KISS_FFT_H
#include <stdlib.h>
#include <stdio.h>
#include <math.h>
#include <string.h>
#ifdef __cplusplus
extern "C" {
#endif
/*
ATTENTION!
If you would like a :
-- a utility that will handle the caching of fft objects
-- real-only (no imaginary time component ) FFT
-- a multi-dimensional FFT
-- a command-line utility to perform ffts
-- a command-line utility to perform fast-convolution filtering
Then see kfc.h kiss_fftr.h kiss_fftnd.h fftutil.c kiss_fastfir.c
in the tools/ directory.
*/
#ifdef USE_SIMD
# include <xmmintrin.h>
# define kiss_fft_scalar __m128
#define KISS_FFT_MALLOC(nbytes) _mm_malloc(nbytes,16)
#define KISS_FFT_FREE _mm_free
#else
#define KISS_FFT_MALLOC malloc
#define KISS_FFT_FREE free
#endif
#ifdef FIXED_POINT
#include <sys/types.h>
# if (FIXED_POINT == 32)
# define kiss_fft_scalar int32_t
# else
# define kiss_fft_scalar int16_t
# endif
#else
# ifndef kiss_fft_scalar
/* default is float */
# define kiss_fft_scalar float
# endif
#endif
#ifndef SOUNDPIPE_H
typedef struct {
kiss_fft_scalar r;
kiss_fft_scalar i;
}kiss_fft_cpx;
typedef struct kiss_fft_state* kiss_fft_cfg;
#endif
/*
* kiss_fft_alloc
*
* Initialize a FFT (or IFFT) algorithm's cfg/state buffer.
*
* typical usage: kiss_fft_cfg mycfg=kiss_fft_alloc(1024,0,NULL,NULL);
*
* The return value from fft_alloc is a cfg buffer used internally
* by the fft routine or NULL.
*
* If lenmem is NULL, then kiss_fft_alloc will allocate a cfg buffer using malloc.
* The returned value should be free()d when done to avoid memory leaks.
*
* The state can be placed in a user supplied buffer 'mem':
* If lenmem is not NULL and mem is not NULL and *lenmem is large enough,
* then the function places the cfg in mem and the size used in *lenmem
* and returns mem.
*
* If lenmem is not NULL and ( mem is NULL or *lenmem is not large enough),
* then the function returns NULL and places the minimum cfg
* buffer size in *lenmem.
* */
kiss_fft_cfg kiss_fft_alloc(int nfft,int inverse_fft,void * mem,size_t * lenmem);
/*
* kiss_fft(cfg,in_out_buf)
*
* Perform an FFT on a complex input buffer.
* for a forward FFT,
* fin should be f[0] , f[1] , ... ,f[nfft-1]
* fout will be F[0] , F[1] , ... ,F[nfft-1]
* Note that each element is complex and can be accessed like
f[k].r and f[k].i
* */
void kiss_fft(kiss_fft_cfg cfg,const kiss_fft_cpx *fin,kiss_fft_cpx *fout);
/*
A more generic version of the above function. It reads its input from every Nth sample.
* */
void kiss_fft_stride(kiss_fft_cfg cfg,const kiss_fft_cpx *fin,kiss_fft_cpx *fout,int fin_stride);
/* If kiss_fft_alloc allocated a buffer, it is one contiguous
buffer and can be simply free()d when no longer needed*/
#define kiss_fft_free free
/*
Cleans up some memory that gets managed internally. Not necessary to call, but it might clean up
your compiler output to call this before you exit.
*/
void kiss_fft_cleanup(void);
/*
* Returns the smallest integer k, such that k>=n and k has only "fast" factors (2,3,5)
*/
int kiss_fft_next_fast_size(int n);
/* for real ffts, we need an even size */
#define kiss_fftr_next_fast_size_real(n) \
(kiss_fft_next_fast_size( ((n)+1)>>1)<<1)
#ifdef __cplusplus
}
#endif
#endif

View File

@@ -0,0 +1,49 @@
#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.
*/
#ifndef SOUNDPIPE_H
typedef struct kiss_fftr_state *kiss_fftr_cfg;
#endif
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

View File

@@ -0,0 +1,409 @@
/*
Copyright (c) 2003-2010, Mark Borgerding
All rights reserved.
Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met:
* Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer.
* Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution.
* Neither the author nor the names of any contributors may be used to endorse or promote products derived from this software without specific prior written permission.
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
#include "_kiss_fft_guts.h"
/* The guts header contains all the multiplication and addition macros that are defined for
fixed or floating point complex numbers. It also delares the kf_ internal functions.
*/
static void kf_bfly2(
kiss_fft_cpx * Fout,
const size_t fstride,
const kiss_fft_cfg st,
int m
)
{
kiss_fft_cpx * Fout2;
kiss_fft_cpx * tw1 = st->twiddles;
kiss_fft_cpx t;
Fout2 = Fout + m;
do{
C_FIXDIV(*Fout,2); C_FIXDIV(*Fout2,2);
C_MUL (t, *Fout2 , *tw1);
tw1 += fstride;
C_SUB( *Fout2 , *Fout , t );
C_ADDTO( *Fout , t );
++Fout2;
++Fout;
}while (--m);
}
static void kf_bfly4(
kiss_fft_cpx * Fout,
const size_t fstride,
const kiss_fft_cfg st,
const size_t m
)
{
kiss_fft_cpx *tw1,*tw2,*tw3;
kiss_fft_cpx scratch[6];
size_t k=m;
const size_t m2=2*m;
const size_t m3=3*m;
tw3 = tw2 = tw1 = st->twiddles;
do {
C_FIXDIV(*Fout,4); C_FIXDIV(Fout[m],4); C_FIXDIV(Fout[m2],4); C_FIXDIV(Fout[m3],4);
C_MUL(scratch[0],Fout[m] , *tw1 );
C_MUL(scratch[1],Fout[m2] , *tw2 );
C_MUL(scratch[2],Fout[m3] , *tw3 );
C_SUB( scratch[5] , *Fout, scratch[1] );
C_ADDTO(*Fout, scratch[1]);
C_ADD( scratch[3] , scratch[0] , scratch[2] );
C_SUB( scratch[4] , scratch[0] , scratch[2] );
C_SUB( Fout[m2], *Fout, scratch[3] );
tw1 += fstride;
tw2 += fstride*2;
tw3 += fstride*3;
C_ADDTO( *Fout , scratch[3] );
if(st->inverse) {
Fout[m].r = scratch[5].r - scratch[4].i;
Fout[m].i = scratch[5].i + scratch[4].r;
Fout[m3].r = scratch[5].r + scratch[4].i;
Fout[m3].i = scratch[5].i - scratch[4].r;
}else{
Fout[m].r = scratch[5].r + scratch[4].i;
Fout[m].i = scratch[5].i - scratch[4].r;
Fout[m3].r = scratch[5].r - scratch[4].i;
Fout[m3].i = scratch[5].i + scratch[4].r;
}
++Fout;
}while(--k);
}
static void kf_bfly3(
kiss_fft_cpx * Fout,
const size_t fstride,
const kiss_fft_cfg st,
size_t m
)
{
size_t k=m;
const size_t m2 = 2*m;
kiss_fft_cpx *tw1,*tw2;
kiss_fft_cpx scratch[5];
kiss_fft_cpx epi3;
epi3 = st->twiddles[fstride*m];
tw1=tw2=st->twiddles;
do{
C_FIXDIV(*Fout,3); C_FIXDIV(Fout[m],3); C_FIXDIV(Fout[m2],3);
C_MUL(scratch[1],Fout[m] , *tw1);
C_MUL(scratch[2],Fout[m2] , *tw2);
C_ADD(scratch[3],scratch[1],scratch[2]);
C_SUB(scratch[0],scratch[1],scratch[2]);
tw1 += fstride;
tw2 += fstride*2;
Fout[m].r = Fout->r - HALF_OF(scratch[3].r);
Fout[m].i = Fout->i - HALF_OF(scratch[3].i);
C_MULBYSCALAR( scratch[0] , epi3.i );
C_ADDTO(*Fout,scratch[3]);
Fout[m2].r = Fout[m].r + scratch[0].i;
Fout[m2].i = Fout[m].i - scratch[0].r;
Fout[m].r -= scratch[0].i;
Fout[m].i += scratch[0].r;
++Fout;
}while(--k);
}
static void kf_bfly5(
kiss_fft_cpx * Fout,
const size_t fstride,
const kiss_fft_cfg st,
int m
)
{
kiss_fft_cpx *Fout0,*Fout1,*Fout2,*Fout3,*Fout4;
int u;
kiss_fft_cpx scratch[13];
kiss_fft_cpx * twiddles = st->twiddles;
kiss_fft_cpx *tw;
kiss_fft_cpx ya,yb;
ya = twiddles[fstride*m];
yb = twiddles[fstride*2*m];
Fout0=Fout;
Fout1=Fout0+m;
Fout2=Fout0+2*m;
Fout3=Fout0+3*m;
Fout4=Fout0+4*m;
tw=st->twiddles;
for ( u=0; u<m; ++u ) {
C_FIXDIV( *Fout0,5); C_FIXDIV( *Fout1,5); C_FIXDIV( *Fout2,5); C_FIXDIV( *Fout3,5); C_FIXDIV( *Fout4,5);
scratch[0] = *Fout0;
C_MUL(scratch[1] ,*Fout1, tw[u*fstride]);
C_MUL(scratch[2] ,*Fout2, tw[2*u*fstride]);
C_MUL(scratch[3] ,*Fout3, tw[3*u*fstride]);
C_MUL(scratch[4] ,*Fout4, tw[4*u*fstride]);
C_ADD( scratch[7],scratch[1],scratch[4]);
C_SUB( scratch[10],scratch[1],scratch[4]);
C_ADD( scratch[8],scratch[2],scratch[3]);
C_SUB( scratch[9],scratch[2],scratch[3]);
Fout0->r += scratch[7].r + scratch[8].r;
Fout0->i += scratch[7].i + scratch[8].i;
scratch[5].r = scratch[0].r + S_MUL(scratch[7].r,ya.r) + S_MUL(scratch[8].r,yb.r);
scratch[5].i = scratch[0].i + S_MUL(scratch[7].i,ya.r) + S_MUL(scratch[8].i,yb.r);
scratch[6].r = S_MUL(scratch[10].i,ya.i) + S_MUL(scratch[9].i,yb.i);
scratch[6].i = -S_MUL(scratch[10].r,ya.i) - S_MUL(scratch[9].r,yb.i);
C_SUB(*Fout1,scratch[5],scratch[6]);
C_ADD(*Fout4,scratch[5],scratch[6]);
scratch[11].r = scratch[0].r + S_MUL(scratch[7].r,yb.r) + S_MUL(scratch[8].r,ya.r);
scratch[11].i = scratch[0].i + S_MUL(scratch[7].i,yb.r) + S_MUL(scratch[8].i,ya.r);
scratch[12].r = - S_MUL(scratch[10].i,yb.i) + S_MUL(scratch[9].i,ya.i);
scratch[12].i = S_MUL(scratch[10].r,yb.i) - S_MUL(scratch[9].r,ya.i);
C_ADD(*Fout2,scratch[11],scratch[12]);
C_SUB(*Fout3,scratch[11],scratch[12]);
++Fout0;++Fout1;++Fout2;++Fout3;++Fout4;
}
}
/* perform the butterfly for one stage of a mixed radix FFT */
static void kf_bfly_generic(
kiss_fft_cpx * Fout,
const size_t fstride,
const kiss_fft_cfg st,
int m,
int p
)
{
int u,k,q1,q;
kiss_fft_cpx * twiddles = st->twiddles;
kiss_fft_cpx t;
int Norig = st->nfft;
kiss_fft_cpx * scratch = (kiss_fft_cpx*)KISS_FFT_TMP_ALLOC(sizeof(kiss_fft_cpx)*p);
for ( u=0; u<m; ++u ) {
k=u;
for ( q1=0 ; q1<p ; ++q1 ) {
scratch[q1] = Fout[ k ];
C_FIXDIV(scratch[q1],p);
k += m;
}
k=u;
for ( q1=0 ; q1<p ; ++q1 ) {
int twidx=0;
Fout[ k ] = scratch[0];
for (q=1;q<p;++q ) {
twidx += fstride * k;
if (twidx>=Norig) twidx-=Norig;
C_MUL(t,scratch[q] , twiddles[twidx] );
C_ADDTO( Fout[ k ] ,t);
}
k += m;
}
}
KISS_FFT_TMP_FREE(scratch);
}
static
void kf_work(
kiss_fft_cpx * Fout,
const kiss_fft_cpx * f,
const size_t fstride,
int in_stride,
int * factors,
const kiss_fft_cfg st
)
{
kiss_fft_cpx * Fout_beg=Fout;
const int p=*factors++; /* the radix */
const int m=*factors++; /* stage's fft length/p */
const kiss_fft_cpx * Fout_end = Fout + p*m;
#ifdef _OPENMP
/* use openmp extensions at the */
/* top-level (not recursive) */
if (fstride==1 && p<=5)
{
int k;
/* execute the p different work units in different threads */
# pragma omp parallel for
for (k=0;k<p;++k)
kf_work( Fout +k*m, f+ fstride*in_stride*k,fstride*p,in_stride,factors,st);
// all threads have joined by this point
switch (p) {
case 2: kf_bfly2(Fout,fstride,st,m); break;
case 3: kf_bfly3(Fout,fstride,st,m); break;
case 4: kf_bfly4(Fout,fstride,st,m); break;
case 5: kf_bfly5(Fout,fstride,st,m); break;
default: kf_bfly_generic(Fout,fstride,st,m,p); break;
}
return;
}
#endif
if (m==1) {
do{
*Fout = *f;
f += fstride*in_stride;
}while(++Fout != Fout_end );
}else{
do{
/* recursive call:
* DFT of size m*p performed by doing
* p instances of smaller DFTs of size m,
* each one takes a decimated version of the input
*/
kf_work( Fout , f, fstride*p, in_stride, factors,st);
f += fstride*in_stride;
}while( (Fout += m) != Fout_end );
}
Fout=Fout_beg;
/* recombine the p smaller DFTs */
switch (p) {
case 2: kf_bfly2(Fout,fstride,st,m); break;
case 3: kf_bfly3(Fout,fstride,st,m); break;
case 4: kf_bfly4(Fout,fstride,st,m); break;
case 5: kf_bfly5(Fout,fstride,st,m); break;
default: kf_bfly_generic(Fout,fstride,st,m,p); break;
}
}
/* facbuf is populated by p1,m1,p2,m2, ...
where
p[i] * m[i] = m[i-1]
m0 = n */
static
void kf_factor(int n,int * facbuf)
{
int p=4;
double floor_sqrt;
floor_sqrt = floor( sqrt((double)n) );
/*factor out powers of 4, powers of 2, then any remaining primes */
do {
while (n % p) {
switch (p) {
case 4: p = 2; break;
case 2: p = 3; break;
default: p += 2; break;
}
if (p > floor_sqrt)
p = n; /* no more factors, skip to end */
}
n /= p;
*facbuf++ = p;
*facbuf++ = n;
} while (n > 1);
}
/*
*
* User-callable function to allocate all necessary storage space for the fft.
*
* The return value is a contiguous block of memory, allocated with malloc. As such,
* It can be freed with free(), rather than a kiss_fft-specific function.
* */
kiss_fft_cfg kiss_fft_alloc(int nfft,int inverse_fft,void * mem,size_t * lenmem )
{
kiss_fft_cfg st=NULL;
size_t memneeded = sizeof(struct kiss_fft_state)
+ sizeof(kiss_fft_cpx)*(nfft-1); /* twiddle factors*/
if ( lenmem==NULL ) {
st = ( kiss_fft_cfg)KISS_FFT_MALLOC( memneeded );
}else{
if (mem != NULL && *lenmem >= memneeded)
st = (kiss_fft_cfg)mem;
*lenmem = memneeded;
}
if (st) {
int i;
st->nfft=nfft;
st->inverse = inverse_fft;
for (i=0;i<nfft;++i) {
const double pi=3.141592653589793238462643383279502884197169399375105820974944;
double phase = -2*pi*i / nfft;
if (st->inverse)
phase *= -1;
kf_cexp(st->twiddles+i, phase );
}
kf_factor(nfft,st->factors);
}
return st;
}
void kiss_fft_stride(kiss_fft_cfg st,const kiss_fft_cpx *fin,kiss_fft_cpx *fout,int in_stride)
{
if (fin == fout) {
/* NOTE: this is not really an in-place FFT algorithm. */
/* It just performs an out-of-place FFT into a temp buffer */
kiss_fft_cpx * tmpbuf = (kiss_fft_cpx*)KISS_FFT_TMP_ALLOC( sizeof(kiss_fft_cpx)*st->nfft);
kf_work(tmpbuf,fin,1,in_stride, st->factors,st);
memcpy(fout,tmpbuf,sizeof(kiss_fft_cpx)*st->nfft);
KISS_FFT_TMP_FREE(tmpbuf);
}else{
kf_work( fout, fin, 1,in_stride, st->factors,st );
}
}
void kiss_fft(kiss_fft_cfg cfg,const kiss_fft_cpx *fin,kiss_fft_cpx *fout)
{
kiss_fft_stride(cfg,fin,fout,1);
}
void kiss_fft_cleanup(void)
{
/* nothing needed any more */
}
int kiss_fft_next_fast_size(int n)
{
while(1) {
int m=n;
while ( (m%2) == 0 ) m/=2;
while ( (m%3) == 0 ) m/=3;
while ( (m%5) == 0 ) m/=5;
if (m<=1)
break; /* n is completely factorable by twos, threes, and fives */
n++;
}
return n;
}

View File

@@ -0,0 +1,159 @@
/*
Copyright (c) 2003-2004, Mark Borgerding
All rights reserved.
Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met:
* Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer.
* Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution.
* Neither the author nor the names of any contributors may be used to endorse or promote products derived from this software without specific prior written permission.
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
#include "kiss_fftr.h"
#include "_kiss_fft_guts.h"
struct kiss_fftr_state{
kiss_fft_cfg substate;
kiss_fft_cpx * tmpbuf;
kiss_fft_cpx * super_twiddles;
#ifdef USE_SIMD
void * pad;
#endif
};
kiss_fftr_cfg kiss_fftr_alloc(int nfft,int inverse_fft,void * mem,size_t * lenmem)
{
int i;
kiss_fftr_cfg st = NULL;
size_t subsize, memneeded;
if (nfft & 1) {
fprintf(stderr,"Real FFT optimization must be even.\n");
return NULL;
}
nfft >>= 1;
kiss_fft_alloc (nfft, inverse_fft, NULL, &subsize);
memneeded = sizeof(struct kiss_fftr_state) + subsize + sizeof(kiss_fft_cpx) * ( nfft * 3 / 2);
if (lenmem == NULL) {
st = (kiss_fftr_cfg) KISS_FFT_MALLOC (memneeded);
} else {
if (*lenmem >= memneeded)
st = (kiss_fftr_cfg) mem;
*lenmem = memneeded;
}
if (!st)
return NULL;
st->substate = (kiss_fft_cfg) (st + 1); /*just beyond kiss_fftr_state struct */
st->tmpbuf = (kiss_fft_cpx *) (((char *) st->substate) + subsize);
st->super_twiddles = st->tmpbuf + nfft;
kiss_fft_alloc(nfft, inverse_fft, st->substate, &subsize);
for (i = 0; i < nfft/2; ++i) {
double phase =
-3.14159265358979323846264338327 * ((double) (i+1) / nfft + .5);
if (inverse_fft)
phase *= -1;
kf_cexp (st->super_twiddles+i,phase);
}
return st;
}
void kiss_fftr(kiss_fftr_cfg st,const kiss_fft_scalar *timedata,kiss_fft_cpx *freqdata)
{
/* input buffer timedata is stored row-wise */
int k,ncfft;
kiss_fft_cpx fpnk,fpk,f1k,f2k,tw,tdc;
if ( st->substate->inverse) {
fprintf(stderr,"kiss fft usage error: improper alloc\n");
exit(1);
}
ncfft = st->substate->nfft;
/*perform the parallel fft of two real signals packed in real,imag*/
kiss_fft( st->substate , (const kiss_fft_cpx*)timedata, st->tmpbuf );
/* The real part of the DC element of the frequency spectrum in st->tmpbuf
* contains the sum of the even-numbered elements of the input time sequence
* The imag part is the sum of the odd-numbered elements
*
* The sum of tdc.r and tdc.i is the sum of the input time sequence.
* yielding DC of input time sequence
* The difference of tdc.r - tdc.i is the sum of the input (dot product) [1,-1,1,-1...
* yielding Nyquist bin of input time sequence
*/
tdc.r = st->tmpbuf[0].r;
tdc.i = st->tmpbuf[0].i;
C_FIXDIV(tdc,2);
CHECK_OVERFLOW_OP(tdc.r ,+, tdc.i);
CHECK_OVERFLOW_OP(tdc.r ,-, tdc.i);
freqdata[0].r = tdc.r + tdc.i;
freqdata[ncfft].r = tdc.r - tdc.i;
#ifdef USE_SIMD
freqdata[ncfft].i = freqdata[0].i = _mm_set1_ps(0);
#else
freqdata[ncfft].i = freqdata[0].i = 0;
#endif
for ( k=1;k <= ncfft/2 ; ++k ) {
fpk = st->tmpbuf[k];
fpnk.r = st->tmpbuf[ncfft-k].r;
fpnk.i = - st->tmpbuf[ncfft-k].i;
C_FIXDIV(fpk,2);
C_FIXDIV(fpnk,2);
C_ADD( f1k, fpk , fpnk );
C_SUB( f2k, fpk , fpnk );
C_MUL( tw , f2k , st->super_twiddles[k-1]);
freqdata[k].r = HALF_OF(f1k.r + tw.r);
freqdata[k].i = HALF_OF(f1k.i + tw.i);
freqdata[ncfft-k].r = HALF_OF(f1k.r - tw.r);
freqdata[ncfft-k].i = HALF_OF(tw.i - f1k.i);
}
}
void kiss_fftri(kiss_fftr_cfg st,const kiss_fft_cpx *freqdata,kiss_fft_scalar *timedata)
{
/* input buffer timedata is stored row-wise */
int k, ncfft;
if (st->substate->inverse == 0) {
fprintf (stderr, "kiss fft usage error: improper alloc\n");
exit (1);
}
ncfft = st->substate->nfft;
st->tmpbuf[0].r = freqdata[0].r + freqdata[ncfft].r;
st->tmpbuf[0].i = freqdata[0].r - freqdata[ncfft].r;
C_FIXDIV(st->tmpbuf[0],2);
for (k = 1; k <= ncfft / 2; ++k) {
kiss_fft_cpx fk, fnkc, fek, fok, tmp;
fk = freqdata[k];
fnkc.r = freqdata[ncfft - k].r;
fnkc.i = -freqdata[ncfft - k].i;
C_FIXDIV( fk , 2 );
C_FIXDIV( fnkc , 2 );
C_ADD (fek, fk, fnkc);
C_SUB (tmp, fk, fnkc);
C_MUL (fok, tmp, st->super_twiddles[k-1]);
C_ADD (st->tmpbuf[k], fek, fok);
C_SUB (st->tmpbuf[ncfft - k], fek, fok);
#ifdef USE_SIMD
st->tmpbuf[ncfft - k].i *= _mm_set1_ps(-1.0);
#else
st->tmpbuf[ncfft - k].i *= -1;
#endif
}
kiss_fft (st->substate, st->tmpbuf, (kiss_fft_cpx *) timedata);
}

76
README.md Normal file
View File

@@ -0,0 +1,76 @@
# ECGSynKit
Swift Package for generating synthetic ECG signals. Includes implementation of ECGSYN [[1]](#1).
# Notes
Implementation roughly corresponds with MATLAB/Java implementation of ECGSYN [[2]](#2) with a few
implementation differences to improve performance. The library is also structured so that expensive
computations can be reused for multiple signals and a single RR series can be reused to generate multi-lead
ECGs.
This implementation generalizes the attractor array to arbitrary size to allow modeling R and S morphologies.
# Usage
Initialize `TimeParameters` and `RRParameters`:
```swift
import ECGSynKit
let timeParameters = TimeParameters(
numBeats: 500,
srInternal: 10, // internal generator sampling rate
decimateFactor: 2, // decimation factor (output signal sample rate will be `srInternal / decimateFactor`)
hrMean: 60, // mean heart rate in BPM
hrStd: 1, // heart rate standard deviation
seed: 1) // rng seed value
let rrParameters = RRParameters(
flo: 0.1, // low frequency oscillation (Mayer wave) in Hz
flostd: 0.01, // low frequency oscillation standard deviation
fhi: 0.25, // high frequency oscillation (respiration) in Hz
fhistd: 0.01, // high frequency oscillation standard deviation
lfhfratio: 0.5) // low frequency to high frequency power ratio
```
Initialize an RR Series generator. This will prepare an FFT setup for the required signal size.
Afterwards generate the RR Series:
``` swift
let rrGenerator = ECGSynRRGenerator(params: timeParameters)
let rrSeries = rrGenerator.generateSeries(params: rrParameters)
```
The `rrSeries` can be used multiple times such as to generate multilead ECG signals.
The `attractors` parameter specify the morphology of the exponential extrema of the ECG. For a normal
ECG there are 5 attractors corresponding to PQRST. The parameters `θ`, `a`, `b` are as described in the paper
and are angular position around the signal limit circle, amplitude, and width. The `θrf` parameter is optional
and is a generalization of the stretching factors for `θ`. Each `θ` will be adjusted by `θ * pow(hrMean / 60.0, θrf)`.
``` swift
let params = ECGSyn.Parameters(
range: (-0.4, 1.2) // the voltage range to scale the signal in mV
noiseAmplitude: 0.01, // the amplitude of the additive uniform noise
attractors: [
// θ, a, b, θrf
.make(deg: -70, 1.2, 0.25, 0.25),
.make(deg: -15, -5.0, 0.1, 0.5),
.make(deg: 0, 30, 0.1),
.make(deg: 15, -7.5, 0.1, 0.5),
.make(deg: 100, 0.75, 0.4, 0.25),
]
)
let signal = ECGSyn.generate(params: timeParameters) // signal is [Double]
```
# References
<a id="1">[1]</a>
McSharry PE, Clifford GD, Tarassenko L, Smith L. A dynamical model for generating synthetic electrocardiogram signals. IEEE Transactions on Biomedical Engineering 50(3): 289-294; March 2003.
<a id="2">[2]</a>
Goldberger, A., Amaral, L., Glass, L., Hausdorff, J., Ivanov, P. C., Mark, R., ... & Stanley, H. E. (2000). PhysioBank, PhysioToolkit, and PhysioNet: Components of a new research resource for complex physiologic signals. Circulation [Online]. 101 (23), pp. e215e220.

View File

@@ -0,0 +1,105 @@
import Foundation
import OdeInt
import RealModule
public struct ECGSyn {
public struct Attractor {
/// Angle of attractor in radians
public let θ: Double
/// Position of extremum above or below the z=0 plane.
public let a: Double
/// Width of the attractor.
public let b: Double
/// Angle rate factor adjustment `θ * pow(hrMean / 60.0, θrf)`
public let θrf: Double
public init(θ: Double, a: Double, b: Double, θrf: Double = 0.0) {
self.θ = θ
self.a = a
self.b = b
self.θrf = θrf
}
public init(deg: Double, a: Double, b: Double, θrf: Double = 0.0) {
self.init(θ: deg * .pi / 180, a: a, b: b, θrf: θrf)
}
static func make(deg: Double, _ a: Double, _ b: Double, _ θrf: Double = 0.0) -> Attractor {
Attractor(deg: deg, a: a, b: b, θrf: θrf)
}
}
public struct Parameters {
/// The ECG amplitude in mV.
public let range: (Double, Double) = (-0.4, 1.2)
/// Amplitude of the noise.
public let noiseAmplitude: Double = 0.0
/// Descriptors of the extrema/attractors for the dynamical model.
public let attractors: [Attractor] = [
.make(deg: -70, 1.2, 0.25, 0.25),
.make(deg: -15, -5.0, 0.1, 0.5),
.make(deg: 0, 30, 0.1),
.make(deg: 15, -7.5, 0.1, 0.5),
.make(deg: 100, 0.75, 0.4, 0.25),
]
}
public static func generate(params: Parameters, rrSeries: ECGSynRRSeries<Double>) -> [Double] {
var rng = rrSeries.rng
let srInternal = rrSeries.timeParameters.srInternal
let hrSec = rrSeries.timeParameters.hrMean / 60.0
let hrFact = sqrt(hrSec)
// adjust extrema parameters for mean heart rate
let ti = params.attractors.map { $0.θ * pow(hrSec, $0.θrf) }
let ai = params.attractors.map { $0.a }
let bi = params.attractors.map { $0.b * hrFact }
let fhi = rrSeries.rrParamaters.fhi
let nt = rrSeries.count
let dt = 1.0 / Double(srInternal)
let ts = (0 ..< nt).map { Double($0) * dt }
let x0 = SIMD3<Double>(1.0, 0.0, 0.04)
let result = SIMD3<Double>.integrate(over: ts, y0: x0, tol: 1e-6) { x, t in
let ta = atan2(x[1], x[0])
let r0 = 1.0
let a0 = 1.0 - sqrt(x[0] * x[0] + x[1] * x[1]) / r0
let w0 = 2 * .pi / rrSeries.valueAt(t)
let zbase = 0.005 * sin(2 * .pi * fhi * t)
var dxdt = SIMD3<Double>(a0 * x[0] - w0 * x[1], a0 * x[1] + w0 * x[0], 0.0)
for i in 0 ..< ti.count {
let dt = remainder(ta - ti[i], 2 * .pi)
dxdt[2] += -ai[i] * dt * exp(-0.5 * (dt * dt) / (bi[i] * bi[i]))
}
dxdt[2] += -1.0 * (x[2] - zbase)
return dxdt
}
// extract z and downsample to output sampling frequency
var zresult = stride(from: 0, to: nt, by: rrSeries.timeParameters.decimateFactor).map { result[$0][2] }
let (zmin, zmax) = zresult.minAndMax()!
let zrange = zmax - zmin
// Scale signal between -0.4 and 1.2 mV
// add uniformly distributed measurement noise
for i in 0 ..< zresult.count {
zresult[i] = (params.range.1 - params.range.0) * (zresult[i] - zmin) / zrange + params.range.0
zresult[i] += params.noiseAmplitude * (2.0 * rng.nextDouble() - 1.0)
}
return zresult
}
}

View File

@@ -0,0 +1,48 @@
import Algorithms
import ComplexModule
import RealModule
import Foundation
import PFFFT
public struct TimeParameters {
/// The number of beats to simulate.
let numBeats: Int = 12
/// The internal sampling frequency in Hz.
let srInternal: Int = 512
/// Output decimation factor
let decimateFactor: Int = 2
/// The mean heart rate in beats per minute.
let hrMean: Double = 60.0
/// The standard deviation of the heart rate.
let hrStd: Double = 1.0
/// RNG seed value.
let seed: UInt64 = 8
}
public struct RRParameters {
/// Mayer wave frequency in Hz.
let flo = 0.1
/// flo standard deviation.
let flostd = 0.01
/// Respiratory rate frequency in Hz.
let fhi = 0.25
/// fhi standard deviation.
let fhistd = 0.01
/// The ratio of power between low and high frequencies.
let lfhfRatio: Double = 0.5
}
func stdev(_ data: [Double]) -> Double {
let n = Double(data.count)
let mean = data.reduce(0.0, +) / n
return sqrt(data.lazy.map { ($0 - mean) * ($0 - mean) }.reduce(0.0, +) / (n - 1))
}

View File

@@ -0,0 +1,81 @@
import ComplexModule
import Foundation
import PFFFT
import RealModule
public struct ECGSynRRGenerator: ~Copyable {
let nrr: Int
let fft: FFT<Double>
let spectrum: Buffer<Complex<Double>>
let signal: Buffer<Double>
var rng: RandomNumberGenerator
// mean and standard deviation of RR intervals
let rrMean: Double
let rrStd: Double
let timeParameters: TimeParameters
public init(params: TimeParameters) {
typealias FFT = PFFFT.FFT<Double>
let sr = params.srInternal
rrMean = 60.0 / params.hrMean
rrStd = 60.0 * params.hrStd / (params.hrMean * params.hrMean)
nrr = FFT.nearestValidSize(params.numBeats * sr * Int(rrMean.rounded(.up)), higher: true)
fft = try! FFT(n: nrr)
spectrum = fft.makeSpectrumBuffer(extra: 1)
signal = fft.makeSignalBuffer()
timeParameters = params
rng = Xoshiro256Plus(seed: params.seed)
}
public mutating func generateSeries(params: RRParameters) -> ECGSynRRSeries<Double> {
let rr = generateSignal(params: params)
return ECGSynRRSeries(timeParameters: timeParameters, rrParamaters: params, rng: rng, signal: rr)
}
public mutating func generateSignal(params: RRParameters) -> [Double] {
let w1 = 2.0 * .pi * params.flo
let w2 = 2.0 * .pi * params.fhi
let c1 = 2.0 * .pi * params.flostd
let c2 = 2.0 * .pi * params.fhistd
let sig2 = 1.0
let sig1 = params.lfhfRatio
let sr = Double(timeParameters.srInternal)
let dw = (sr / Double(nrr)) * 2.0 * .pi
spectrum.mapInPlaceSwapLast { i in
let w = dw * Double(i)
let dw1 = w - w1
let dw2 = w - w2
let hw = sig1 * exp(-dw1 * dw1 / (2.0 * c1 * c1)) / sqrt(2.0 * .pi * c1 * c1)
+ sig2 * exp(-dw2 * dw2 / (2.0 * c2 * c2)) / sqrt(2.0 * .pi * c2 * c2)
let sw = (sr / 2.0) * sqrt(hw)
let ph = 2.0 * .pi * rng.nextDouble()
return Complex(length: sw, phase: ph)
}
fft.inverse(spectrum: spectrum, signal: signal)
var rr = signal.map { $0 * 1.0 / Double(nrr) }
let xstd = stdev(rr)
let ratio = rrStd / xstd
for i in 0 ..< nrr {
rr[i] = rr[i] * ratio + rrMean
}
return rr
}
}

View File

@@ -0,0 +1,46 @@
import Foundation
import RealModule
import Algorithms
public struct ECGSynRRSeries<T: BinaryFloatingPoint> {
let timeParameters: TimeParameters
let rrParamaters: RRParameters
let rng: RandomNumberGenerator
let count: Int
struct Segment {
let end: T
let value: T
}
let segments: [Segment]
public init(timeParameters: TimeParameters, rrParamaters: RRParameters, rng: RandomNumberGenerator, signal: [T]) {
self.timeParameters = timeParameters
self.rrParamaters = rrParamaters
self.rng = rng
let sr = T(timeParameters.srInternal)
var rrn = [Segment]()
// generate piecewise RR time series
do {
var tecg = T.zero
var i = 0
while i < signal.count {
tecg += signal[i]
rrn.append(Segment(end: tecg, value: signal[i]))
i = Int((tecg * sr).rounded(.toNearestOrEven)) + 1
}
}
segments = rrn
count = signal.count
}
@inline(__always)
public func valueAt(_ t: T) -> T {
let index = min(segments.partitioningIndex { t < $0.end }, segments.endIndex - 1)
return segments[index].value
}
}

View File

@@ -0,0 +1,9 @@
extension RandomNumberGenerator {
mutating func nextDouble() -> Double {
Double(next() >> 11) * 0x1.0p-53
}
mutating func nextFloat() -> Float {
Float(next() >> 40) * 0x1.0p-24
}
}

View File

@@ -0,0 +1,21 @@
struct SplitMix64 : RandomNumberGenerator {
public typealias State = UInt64
public private(set) var state: State
init(state: UInt64) {
self.state = state
}
public mutating func next() -> UInt64 {
state &+= 0x9E3779B97F4A7C15
var z = state
z = (z ^ (z >> 30)) &* 0xBF58476D1CE4E5B9
z = (z ^ (z >> 27)) &* 0x94D049BB133111EB
return z ^ (z >> 31)
}
public mutating func nextDouble() -> Double {
Double(next() >> 11) * 0x1.0p-53
}
}

View File

@@ -0,0 +1,59 @@
import Foundation
public enum Xoshiro256: Equatable {
public typealias State = (UInt64, UInt64, UInt64, UInt64)
internal static var invalidState: State { (0, 0, 0, 0) }
internal static func isValid(state: State) -> Bool {
state != invalidState
}
}
@inlinable
@inline(__always)
internal func rotl(_ x: UInt64, _ k: UInt64) -> UInt64 {
(x << k) | (x >> (64 &- k))
}
struct Xoshiro256Plus: RandomNumberGenerator {
public typealias State = Xoshiro256.State
private var state: State
public init() {
var generator = SystemRandomNumberGenerator()
self.init(seed: generator.next())
}
public init(seed: UInt64) {
var generator = SplitMix64(state: seed)
var state = Xoshiro256.invalidState
repeat {
state = (generator.next(), generator.next(), generator.next(), generator.next())
} while !Xoshiro256.isValid(state: state)
self.init(state: state)
}
public init(state: State) {
precondition(Xoshiro256.isValid(state: state), "The state must not be zero")
self.state = state
}
public mutating func next() -> UInt64 {
let result = state.0 &+ state.3
let t = state.1 << 17
state.2 ^= state.0
state.3 ^= state.1
state.1 ^= state.2
state.0 ^= state.3
state.2 ^= t
state.3 = rotl(state.3, 45)
return result
}
}

View File

@@ -0,0 +1,19 @@
import Testing
@testable import ECGSynKit
import PFFFT
import ComplexModule
import Foundation
@Test func ecgSynTest () {
let timeParameters = TimeParameters()
let rrParameters = RRParameters()
var rrg = ECGSynRRGenerator(params: timeParameters)
let parameters = ECGSyn.Parameters()
let ecg = ECGSyn.generate(params: parameters, rrSeries: rrg.generateSeries(params: rrParameters))
// write ecg to file
let url = URL(fileURLWithPath: "ecg.txt")
let ecgString = ecg.map { String($0) }.joined(separator: "\n")
try! ecgString.write(to: url, atomically: true, encoding: .utf8)
}

11
ecgsyn-1.0.0/C/index.html Normal file
View File

@@ -0,0 +1,11 @@
<html>
<head><title>Index of /static/published-projects/ecgsyn/1.0.0/C/</title></head>
<body>
<h1>Index of /static/published-projects/ecgsyn/1.0.0/C/</h1><hr><pre><a href="../">../</a>
<a href="linux/">linux/</a> 12-Apr-2019 17:35 -
<a href="solaris/">solaris/</a> 12-Apr-2019 17:35 -
<a href="src/">src/</a> 12-Apr-2019 17:35 -
<a href="windows/">windows/</a> 12-Apr-2019 17:35 -
<a href="index.shtml">index.shtml</a> 12-Apr-2019 17:35 4274
</pre><hr></body>
</html>

114
ecgsyn-1.0.0/C/index.shtml Normal file
View File

@@ -0,0 +1,114 @@
<!--#set var="TITLE" value="Compiling and using the C version of ECGSYN"-->
<!--#include virtual="/head.shtml"-->
<h2>Compiling ECGSYN</h2>
<p>
Sources for the C version of <a href="../">ECGSYN</a> are <a
href="src/">here</a>. Note, however, that two additional files are required in
order to compile ECGSYN. These files (<tt>dfour1.c</tt> and <tt>ran1.c</tt>)
are those included in <a href="http://www.nr.com" target="other">Numerical
Recipes in C</a>. Before attempting to compile ECGSYN, obtain these two files
from Numerical Recipes in C and place them in the same directory as the other
sources. (These files are not provided here because they cannot be freely
redistributed.)
<p>
If you cannot obtain <tt>dfour1.c</tt> and <tt>ran1.c</tt>, several
alternatives are available, including ready-to-run executable versions of
ECGSYN for <a href="linux/">GNU/Linux</a>, <a href="solaris/">Solaris</a>, and
<a href="windows/">MS-Windows</a>, as well as <a
href="../Matlab/">Matlab/Octave</a> and <a href="../Java/">Java</a>
implementations of ECGSYN.
<p>
On most platforms, including GNU/Linux, MacOS X, and Unix, compile ECGSYN
by typing
<pre>
make
</pre>
in a terminal window. This will also work under MS-Windows if you have
installed the free <a href="http://www.cygwin.com" target="other">Cygwin/gcc
development toolkit</a>. If you wish to use another compiler under MS-Windows,
you are on your own.
<h2>Running ECGSYN</h2>
<p>
The executable version is named <tt>ecgsyn</tt> (or, under MS-Windows,
<tt>ecgsyn.exe</tt>). Once you have this file, move it into any directory in
your PATH and run it by typing
<pre>
ecgsyn
</pre>
in a terminal window. You may add options (switches) to the '<tt>ecgsyn</tt>'
command; the option '<tt>$</tt>', as in
<pre>
ecgsyn $
</pre>
starts ECGSYN in interactive mode, as shown below:
<pre>
>> ecgsyn $
ECGSYN: A program for generating a realistic synthetic ECG
Copyright (c) 2003 by Patrick McSharry & Gari Clifford. All rights reserved.
O Name of output data file "ecgsyn.dat"
n Approximate number of heart beats 256
s ECG sampling frequency [Hz] 256
S Internal Sampling frequency [Hz] 256
a Amplitude of additive uniform noise [mV] 0
h Heart rate mean [bpm] 60
H Heart rate standard deviation [bpm] 1
f Low frequency [Hz] 0.1
F High frequency [Hz] 0.25
v Low frequency standard deviation [Hz] 0.01
V High frequency standard deviation [Hz] 0.01
q LF/HF ratio 0.5
R Seed 1
(Type ? for Help)
->
</pre>
<p>
At the prompt, you can then type <tt>?</tt> for help, or simply specify each
parameter that you want to change by typing the corresponding option letter (in
the first column above) followed by a space and the new value, then
&lt;enter&gt;. To review the current settings, enter a blank line. After you
have modified all the parameters you want, type = followed by &lt;enter&gt; to
run the program. You may also specify the parameters in a file called
<tt>ecgsyn.opt</tt>, which is automatically read (if it exists) the next time
you run the program.
<p>
Any of the options can also be entered directly on the command line, by
prefixing the option letter with '<tt>-</tt>', as in:
<pre>
ecgsyn -h 80 -n 100
</pre>
(which would create output at a mean heart rate of 80 bpm, lasting for about
100 beats).
<p>
The source files <tt>opt.c</tt> and <tt>opt.h</tt> provide the option
interpreter for ECGSYN; they were written by James Theiler. For additional
information, and for the most recent version of <tt>opt</tt>, see
<a href="http://nis-www.lanl.gov/~jt/Software/"
target="other"><tt>http://nis-www.lanl.gov/~jt/Software/</tt></a>.
<h2>Interpreting ECGSYN's output</h2>
<p>
Unless you have changed the name of the output file with the '<tt>O</tt>'
parameter, you will find the synthetic ECG written to a text file called
<tt>ecgsyn.dat</tt>. Click <a href="../sample-output/">here</a> for a sample
of ECGSYN output and for a description of the format of this file.
<hr>
<!--#include virtual="/footer.shtml"-->
</body>
</html>

View File

@@ -0,0 +1,10 @@
<!--#set var="TITLE" value="ECGSYN for GNU/Linux"-->
<!--#include virtual="/head.shtml"-->
<p>
The executable <tt>ecgsyn</tt> file in this directory was compiled from
these <a href="../src/">ECGSYN sources</a> with gcc 2.96 under Red Hat
Linux 7.2. It is known to work under Red Hat Linux 7.1, 7.2, 7.3, 8, and 9,
and may also work under other versions of GNU/Linux.

BIN
ecgsyn-1.0.0/C/linux/ecgsyn Normal file

Binary file not shown.

View File

@@ -0,0 +1,8 @@
<html>
<head><title>Index of /static/published-projects/ecgsyn/1.0.0/C/linux/</title></head>
<body>
<h1>Index of /static/published-projects/ecgsyn/1.0.0/C/linux/</h1><hr><pre><a href="../">../</a>
<a href="HEADER.shtml">HEADER.shtml</a> 12-Apr-2019 17:35 371
<a href="ecgsyn">ecgsyn</a> 12-Apr-2019 17:35 46457
</pre><hr></body>
</html>

View File

@@ -0,0 +1,9 @@
<!--#set var="TITLE" value="ECGSYN for Solaris"-->
<!--#include virtual="/head.shtml"-->
<p>
The executable <tt>ecgsyn</tt> file in this directory was compiled from
these <a href="../src/">ECGSYN sources</a> with gcc 3.1 under SunOS 5.8
(Solaris 8).

Binary file not shown.

View File

@@ -0,0 +1,8 @@
<html>
<head><title>Index of /static/published-projects/ecgsyn/1.0.0/C/solaris/</title></head>
<body>
<h1>Index of /static/published-projects/ecgsyn/1.0.0/C/solaris/</h1><hr><pre><a href="../">../</a>
<a href="HEADER.shtml">HEADER.shtml</a> 12-Apr-2019 17:35 253
<a href="ecgsyn">ecgsyn</a> 12-Apr-2019 17:35 43854
</pre><hr></body>
</html>

116
ecgsyn-1.0.0/C/src/00README Normal file
View File

@@ -0,0 +1,116 @@
ECGSYN: A program for generating a realistic synthetic ECG
Copyright (C) 2003 by Patrick McSharry & Gari Clifford. All rights reserved.
Compiling ECGSYN
================
This directory contains sources for the C version of ECGSYN. Note, however,
that two additional files are required in order to compile ECGSYN. These
files (dfour1.c and ran1.c) are those included in Numerical Recipes in C
(http://www.nr.com/). Before attempting to compile ECGSYN, obtain these
two files from Numerical Recipes in C and place them in this directory.
(These files are missing from this directory because they cannot be freely
redistributed.)
If you cannot obtain dfour1.c and ran1.c, several alternatives are available,
including ready-to-run executable versions of ECGSYN for GNU/Linux, Solaris,
and MS-Windows (see the directory above this one), as well as Matlab/Octave
and Java implementations of ECGSYN. See http://www.physionet.org/ for details.
On most platforms, including GNU/Linux, MacOS X, and Unix, compile ECGSYN
by typing
make
in a terminal window. This will also work under MS-Windows if you have
installed the free Cygwin/gcc development toolkit (http://www.cygwin.com/).
If you wish to use another compiler under MS-Windows, you are on your own.
Running ECGSYN
==============
The executable version is named 'ecgsyn' (or, under MS-Windows, 'ecgsyn.exe').
Once you have this file, move it into any directory in your PATH and run
it by typing
ecgsyn
in a terminal window. You may add options (switches) to the 'ecgsyn' command;
the option '$', as in
ecgsyn $
starts 'ecgsyn' in interactive mode, as shown below:
>> ecgsyn $
ECGSYN: A program for generating a realistic synthetic ECG
Copyright (c) 2003 by Patrick McSharry & Gari Clifford. All rights reserved.
O Name of output data file "ecgsyn.dat"
n Approximate number of heart beats 256
s ECG sampling frequency [Hz] 256
S Internal Sampling frequency [Hz] 256
a Amplitude of additive uniform noise [mV] 0
h Heart rate mean [bpm] 60
H Heart rate standard deviation [bpm] 1
f Low frequency [Hz] 0.1
F High frequency [Hz] 0.25
v Low frequency standard deviation [Hz] 0.01
V High frequency standard deviation [Hz] 0.01
q LF/HF ratio 0.5
R Seed 1
(Type ? for Help)
->
At the prompt, you can then type ? for help, or simply specify each parameter
above that you want to change by typing the corresponding option letter (in the
first column above) followed by a space and the new value, then <enter>. To
review the current settings, enter a blank line. After you have modified all
the parameters you want, type = followed by <enter> to run the program. You may
also specify the parameters in a file called ecgsyn.opt, which is automatically
read (if it exists) the next time you run the program.
Any of the options can also be entered directly on the command line, by
prefixing the option letter with '-', as in:
ecgsyn -h 80 -n 100
(which would create output at a mean heart rate of 80 bpm, lasting for about
100 beats).
The source files opt.c and opt.h provide the option interpreter for ecgsyn;
they were written by James Theiler. For additional information, and for the
most recent version of 'opt', see http://nis-www.lanl.gov/~jt/Software/.
Interpreting ECGSYN's output
============================
Unless you have changed the name of the output file with the 'O' parameter,
you will find the synthetic ECG written to a text file called ecgsyn.dat,
which contains three columns:
* the time in seconds
* the ECG amplitude in millivolts
* markers to indicate the locations of the waveform peaks:
0 - (no peak)
1 - Top of P-wave
2 - Q-point
3 - Peak of R-wave
4 - S-point
5 - Peak of T-wave
To convert the ECG output into PhysioBank (WFDB) format, use a command
such as
wrsamp -F 256 -o synth -x 200 1 <ecgsyn.dat
(assuming you have not changed the default sampling frequency of 256 Hz).
This command produces a record named 'synth' (a signal file named 'synth.dat'
and a header file named 'synth.hea'), which can be read by other PhysioToolkit
applications. 'wrsamp' is part of the WFDB software package; see
http://www.physionet.org/physiotools/wfdb.shtml for details.
Further information
===================
The most recent version of this software can always be obtained from
http://www.physionet.org/physiotools/ecgsyn/
See IEEE Transactions on Biomedical Engineering, 50(3),289-294, March 2003.
Authors: P. McSharry (patrick AT mcsharry DOT net)
G. Clifford (gari AT mit DOT edu)

View File

@@ -0,0 +1,19 @@
# file: Makefile
# 'make' description file for compiling ecgsyn
CFILES = ecgsyn.c opt.c dfour1.c ran1.c
CFLAGS = -O
ecgsyn: $(CFILES) opt.h
$(CC) $(CFLAGS) -o ecgsyn $(CFILES) -lm
dfour1.c ran1.c:
@echo "To compile ecgsyn, first get copies of dfour1.c and ran1.c"
@echo "(from Numerical Recipes in C) and place them in this directory."
@echo "Then type:"
@echo " make ecgsyn"
@exit 1
clean:
rm -f *~ *.o *.obj

664
ecgsyn-1.0.0/C/src/ecgsyn.c Normal file
View File

@@ -0,0 +1,664 @@
/* 09 Oct 2003 "ecgsyn.c" */
/* */
/* Copyright (c)2003 by Patrick McSharry & Gari Clifford, All Rights Reserved */
/* See IEEE Transactions On Biomedical Engineering, 50(3),289-294, March 2003.*/
/* Contact P. McSharry (patrick AT mcsharry DOT net) or */
/* G. Clifford (gari AT mit DOT edu) */
/* */
/* This program is free software; you can redistribute it and/or modify */
/* it under the terms of the GNU General Public License as published by */
/* the Free Software Foundation; either version 2 of the License, or */
/* (at your option) any later version. */
/* */
/* 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 General Public License */
/* along with this program; if not, write to the Free Software Foundation */
/* Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */
/* */
/* ecgsyn.m and its dependents are freely availble from Physionet - */
/* http://www.physionet.org/ - please report any bugs to the authors above. */
#include <stdio.h>
#include <math.h>
#include <stdlib.h>
#include "opt.h"
#define PI (2.0*asin(1.0))
#define SWAP(a,b) tempr=(a);(a)=(b);(b)=tempr
#define MIN(a,b) (a < b ? a : b)
#define MAX(a,b) (a > b ? a : b)
#define PI (2.0*asin(1.0))
#define OFFSET 1
#define ARG1 char*
#define IA 16807
#define IM 2147483647
#define AM (1.0/IM)
#define IQ 127773
#define IR 2836
#define NTAB 32
#define NDIV (1+(IM-1)/NTAB)
#define EPS 1.2e-7
#define RNMX (1.0-EPS)
/*--------------------------------------------------------------------------*/
/* DEFINE PARAMETERS AS GLOBAL VARIABLES */
/*--------------------------------------------------------------------------*/
char outfile[100]="ecgsyn.dat";/* Output data file */
int N = 256; /* Number of heart beats */
int sfecg = 256; /* ECG sampling frequency */
int sf = 256; /* Internal sampling frequency */
double Anoise = 0.0; /* Amplitude of additive uniform noise*/
double hrmean = 60.0; /* Heart rate mean */
double hrstd = 1.0; /* Heart rate std */
double flo = 0.1; /* Low frequency */
double fhi = 0.25; /* High frequency */
double flostd = 0.01; /* Low frequency std */
double fhistd = 0.01; /* High frequency std */
double lfhfratio = 0.5; /* LF/HF ratio */
int Necg = 0; /* Number of ECG outputs */
int mstate = 3; /* System state space dimension */
double xinitial = 1.0; /* Initial x co-ordinate value */
double yinitial = 0.0; /* Initial y co-ordinate value */
double zinitial = 0.04; /* Initial z co-ordinate value */
int seed = 1; /* Seed */
long rseed;
double h;
double *rr,*rrpc;
double *ti,*ai,*bi;
/* prototypes for externally defined functions */
void dfour1(double data[], unsigned long nn, int isign);
float ran1(long *idum);
/*---------------------------------------------------------------------------*/
/* ALLOCATE MEMORY FOR VECTOR */
/*---------------------------------------------------------------------------*/
double *mallocVect(long n0, long nx)
{
double *vect;
vect=(double *)malloc((size_t) ((nx-n0+1+OFFSET)*sizeof(double)));
if (!vect){
printf("Memory allocation failure in mallocVect");
}
return vect-n0+OFFSET;
}
/*---------------------------------------------------------------------------*/
/* FREE MEMORY FOR MALLOCVECT */
/*---------------------------------------------------------------------------*/
void freeVect(double *vect, long n0, long nx)
{
free((ARG1) (vect+n0-OFFSET));
}
/*---------------------------------------------------------------------------*/
/* MEAN CALCULATOR */
/*---------------------------------------------------------------------------*/
double mean(double *x, int n)
/* n-by-1 vector, calculate mean */
{
int j;
double add;
add = 0.0;
for(j=1;j<=n;j++) add += x[j];
return (add/n);
}
/*---------------------------------------------------------------------------*/
/* STANDARD DEVIATION CALCULATOR */
/*---------------------------------------------------------------------------*/
double stdev(double *x, int n)
/* n-by-1 vector, calculate standard deviation */
{
int j;
double add,mean,diff,total;
add = 0.0;
for(j=1;j<=n;j++) add += x[j];
mean = add/n;
total = 0.0;
for(j=1;j<=n;j++)
{
diff = x[j] - mean;
total += diff*diff;
}
return (sqrt(total/(n-1)));
}
/*--------------------------------------------------------------------------*/
/* WRITE VECTOR IN A FILE */
/*--------------------------------------------------------------------------*/
void vecfile(char filename[], double *x, int n)
{
int i;
FILE *fp;
fp = fopen(filename,"w");
for(i=1;i<=n;i++) fprintf(fp,"%e\n",x[i]);
fclose(fp);
}
/*--------------------------------------------------------------------------*/
/* INTERP */
/*--------------------------------------------------------------------------*/
void interp(double *y, double *x, int n, int r)
{
int i,j;
double a;
for(i=1;i<=n-1;i++)
{
for(j=1;j<=r;j++)
{
a = (j-1)*1.0/r;
y[(i-1)*r+j] = (1.0-a)*x[i] + a*x[i+1];
}
}
}
/*--------------------------------------------------------------------------*/
/* GENERATE RR PROCESS */
/*--------------------------------------------------------------------------*/
void rrprocess(double *rr, double flo, double fhi,
double flostd, double fhistd, double lfhfratio,
double hrmean, double hrstd, double sf, int n)
{
int i,j;
double c1,c2,w1,w2,sig1,sig2,rrmean,rrstd,xstd,ratio;
double df,dw1,dw2,*w,*Hw,*Sw,*ph0,*ph,*SwC;
w = mallocVect(1,n);
Hw = mallocVect(1,n);
Sw = mallocVect(1,n);
ph0 = mallocVect(1,n/2-1);
ph = mallocVect(1,n);
SwC = mallocVect(1,2*n);
w1 = 2.0*PI*flo;
w2 = 2.0*PI*fhi;
c1 = 2.0*PI*flostd;
c2 = 2.0*PI*fhistd;
sig2 = 1.0;
sig1 = lfhfratio;
rrmean = 60.0/hrmean;
rrstd = 60.0*hrstd/(hrmean*hrmean);
df = sf/n;
for(i=1;i<=n;i++) w[i] = (i-1)*2.0*PI*df;
for(i=1;i<=n;i++)
{
dw1 = w[i]-w1;
dw2 = w[i]-w2;
Hw[i] = sig1*exp(-dw1*dw1/(2.0*c1*c1))/sqrt(2*PI*c1*c1)
+ sig2*exp(-dw2*dw2/(2.0*c2*c2))/sqrt(2*PI*c2*c2);
}
for(i=1;i<=n/2;i++) Sw[i] = (sf/2.0)*sqrt(Hw[i]);
for(i=n/2+1;i<=n;i++) Sw[i] = (sf/2.0)*sqrt(Hw[n-i+1]);
/* randomise the phases */
for(i=1;i<=n/2-1;i++) ph0[i] = 2.0*PI*ran1(&rseed);
ph[1] = 0.0;
for(i=1;i<=n/2-1;i++) ph[i+1] = ph0[i];
ph[n/2+1] = 0.0;
for(i=1;i<=n/2-1;i++) ph[n-i+1] = - ph0[i];
/* make complex spectrum */
for(i=1;i<=n;i++) SwC[2*i-1] = Sw[i]*cos(ph[i]);
for(i=1;i<=n;i++) SwC[2*i] = Sw[i]*sin(ph[i]);
/* calculate inverse fft */
dfour1(SwC,n,-1);
/* extract real part */
for(i=1;i<=n;i++) rr[i] = (1.0/n)*SwC[2*i-1];
xstd = stdev(rr,n);
ratio = rrstd/xstd;
for(i=1;i<=n;i++) rr[i] *= ratio;
for(i=1;i<=n;i++) rr[i] += rrmean;
freeVect(w,1,n);
freeVect(Hw,1,n);
freeVect(Sw,1,n);
freeVect(ph0,1,n/2-1);
freeVect(ph,1,n);
freeVect(SwC,1,2*n);
}
/*--------------------------------------------------------------------------*/
/* THE ANGULAR FREQUENCY */
/*--------------------------------------------------------------------------*/
double angfreq(double t)
{
int i;
i = 1 + (int)floor(t/h);
return 2.0*PI/rrpc[i];
}
/*--------------------------------------------------------------------------*/
/* THE EXACT NONLINEAR DERIVATIVES */
/*--------------------------------------------------------------------------*/
void derivspqrst(double t0,double x[],double dxdt[])
{
int i,k;
double a0,w0,r0,x0,y0,z0;
double t,dt,dt2,*xi,*yi,zbase;
k = 5;
xi = mallocVect(1,k);
yi = mallocVect(1,k);
w0 = angfreq(t0);
r0 = 1.0; x0 = 0.0; y0 = 0.0; z0 = 0.0;
a0 = 1.0 - sqrt((x[1]-x0)*(x[1]-x0) + (x[2]-y0)*(x[2]-y0))/r0;
for(i=1;i<=k;i++) xi[i] = cos(ti[i]);
for(i=1;i<=k;i++) yi[i] = sin(ti[i]);
zbase = 0.005*sin(2.0*PI*fhi*t0);
t = atan2(x[2],x[1]);
dxdt[1] = a0*(x[1] - x0) - w0*(x[2] - y0);
dxdt[2] = a0*(x[2] - y0) + w0*(x[1] - x0);
dxdt[3] = 0.0;
for(i=1;i<=k;i++)
{
dt = fmod(t-ti[i],2.0*PI);
dt2 = dt*dt;
dxdt[3] += -ai[i]*dt*exp(-0.5*dt2/(bi[i]*bi[i]));
}
dxdt[3] += -1.0*(x[3] - zbase);
freeVect(xi,1,k);
freeVect(yi,1,k);
}
/*--------------------------------------------------------------------------*/
/* RUNGA-KUTTA FOURTH ORDER INTEGRATION */
/*--------------------------------------------------------------------------*/
void drk4(double y[], int n, double x, double h, double yout[],
void (*derivs)(double, double [], double []))
{
int i;
double xh,hh,h6,*dydx,*dym,*dyt,*yt;
dydx=mallocVect(1,n);
dym=mallocVect(1,n);
dyt=mallocVect(1,n);
yt=mallocVect(1,n);
hh=h*0.5;
h6=h/6.0;
xh=x+hh;
(*derivs)(x,y,dydx);
for (i=1;i<=n;i++) yt[i]=y[i]+hh*dydx[i];
(*derivs)(xh,yt,dyt);
for (i=1;i<=n;i++) yt[i]=y[i]+hh*dyt[i];
(*derivs)(xh,yt,dym);
for (i=1;i<=n;i++) {
yt[i]=y[i]+h*dym[i];
dym[i] += dyt[i];
}
(*derivs)(x+h,yt,dyt);
for (i=1;i<=n;i++)
yout[i]=y[i]+h6*(dydx[i]+dyt[i]+2.0*dym[i]);
freeVect(dydx,1,n);
freeVect(dym,1,n);
freeVect(dyt,1,n);
freeVect(yt,1,n);
}
/*--------------------------------------------------------------------------*/
/* DETECT PEAKS */
/*--------------------------------------------------------------------------*/
double detectpeaks(double *ipeak, double *x, double *y, double *z, int n)
{
int i,j,j1,j2,jmin,jmax,d;
double thetap1,thetap2,thetap3,thetap4,thetap5;
double theta1,theta2,d1,d2,zmin,zmax;
/* use globally defined angles for PQRST */
thetap1 = ti[1];
thetap2 = ti[2];
thetap3 = ti[3];
thetap4 = ti[4];
thetap5 = ti[5];
for(i=1;i<=n;i++) ipeak[i] = 0.0;
theta1 = atan2(y[1],x[1]);
for(i=1;i<n;i++)
{
theta2 = atan2(y[i+1],x[i+1]);
if( (theta1 <= thetap1) && (thetap1 <= theta2) )
{
d1 = thetap1 - theta1;
d2 = theta2 - thetap1;
if(d1 < d2) ipeak[i] = 1.0;
else ipeak[i+1] = 1.0;
}
else if( (theta1 <= thetap2) && (thetap2 <= theta2) )
{
d1 = thetap2 - theta1;
d2 = theta2 - thetap2;
if(d1 < d2) ipeak[i] = 2.0;
else ipeak[i+1] = 2.0;
}
else if( (theta1 <= thetap3) && (thetap3 <= theta2) )
{
d1 = thetap3 - theta1;
d2 = theta2 - thetap3;
if(d1 < d2) ipeak[i] = 3.0;
else ipeak[i+1] = 3.0;
}
else if( (theta1 <= thetap4) && (thetap4 <= theta2) )
{
d1 = thetap4 - theta1;
d2 = theta2 - thetap4;
if(d1 < d2) ipeak[i] = 4.0;
else ipeak[i+1] = 4.0;
}
else if( (theta1 <= thetap5) && (thetap5 <= theta2) )
{
d1 = thetap5 - theta1;
d2 = theta2 - thetap5;
if(d1 < d2) ipeak[i] = 5.0;
else ipeak[i+1] = 5.0;
}
theta1 = theta2;
}
/* correct the peaks */
d = (int)ceil(sfecg/64);
for(i=1;i<=n;i++)
{
if( ipeak[i]==1 || ipeak[i]==3 || ipeak[i]==5 )
{
j1 = MAX(1,i-d);
j2 = MIN(n,i+d);
jmax = j1;
zmax = z[j1];
for(j=j1+1;j<=j2;j++)
{
if(z[j] > zmax)
{
jmax = j;
zmax = z[j];
}
}
if(jmax != i)
{
ipeak[jmax] = ipeak[i];
ipeak[i] = 0;
}
}
else if( ipeak[i]==2 || ipeak[i]==4 )
{
j1 = MAX(1,i-d);
j2 = MIN(n,i+d);
jmin = j1;
zmin = z[j1];
for(j=j1+1;j<=j2;j++)
{
if(z[j] < zmin)
{
jmin = j;
zmin = z[j];
}
}
if(jmin != i)
{
ipeak[jmin] = ipeak[i];
ipeak[i] = 0;
}
}
}
}
/*--------------------------------------------------------------------------*/
/* MAIN PROGRAM */
/*---------------------------------------------------------------------------*/
main(argc,argv)
int argc;
char **argv;
{
/* First step is to register the options */
optregister(outfile,CSTRING,'O',"Name of output data file");
optregister(N,INT,'n',"Approximate number of heart beats");
optregister(sfecg,INT,'s',"ECG sampling frequency [Hz]");
optregister(sf,INT,'S',"Internal Sampling frequency [Hz]");
optregister(Anoise,DOUBLE,'a',"Amplitude of additive uniform noise [mV]");
optregister(hrmean,DOUBLE,'h',"Heart rate mean [bpm]");
optregister(hrstd,DOUBLE,'H',"Heart rate standard deviation [bpm]");
optregister(flo,DOUBLE,'f',"Low frequency [Hz]");
optregister(fhi,DOUBLE,'F',"High frequency [Hz]");
optregister(flostd,DOUBLE,'v',"Low frequency standard deviation [Hz]");
optregister(fhistd,DOUBLE,'V',"High frequency standard deviation [Hz]");
optregister(lfhfratio,DOUBLE,'q',"LF/HF ratio");
optregister(seed,INT,'R',"Seed");
opt_title_set("ECGSYN: A program for generating a realistic synthetic ECG\n"
"Copyright (c) 2003 by Patrick McSharry & Gari Clifford. All rights reserved.\n");
getopts(argc,argv);
dorun();
}
/*--------------------------------------------------------------------------*/
/* DORUN PART OF PROGRAM */
/*--------------------------------------------------------------------------*/
int dorun()
{
int i,j,k,q,Nrr,Nt,Nts;
double *x,tstep,tecg,rrmean,qd,hrfact,hrfact2;
double *xt,*yt,*zt,*xts,*yts,*zts;
double timev,*ipeak,zmin,zmax,zrange;
FILE *fp;
void (*derivs)(double, double [], double []);
/* perform some checks on input values */
q = (int)rint(sf/sfecg);
qd = (double)sf/(double)sfecg;
if(q != qd) {
printf("Internal sampling frequency must be an integer multiple of the \n");
printf("ECG sampling frequency!\n");
printf("Your current choices are:\n");
printf("ECG sampling frequency: %d Hertz\n",sfecg);
printf("Internal sampling frequency: %d Hertz\n",sf);
exit(1);}
/* declare and initialise the state vector */
x=mallocVect(1,mstate);
x[1] = xinitial;
x[2] = yinitial;
x[3] = zinitial;
/* declare and define the ECG morphology vectors (PQRST extrema parameters) */
ti=mallocVect(1,5);
ai=mallocVect(1,5);
bi=mallocVect(1,5);
/* P Q R S T */
ti[1]=-60.0; ti[2]=-15.0; ti[3]=0.0; ti[4]=15.0; ti[5]=90.0;
ai[1]=1.2; ai[2]=-5.0; ai[3]=30.0; ai[4]=-7.5; ai[5]=0.75;
bi[1]=0.25; bi[2]=0.1; bi[3]=0.1; bi[4]=0.1; bi[5]=0.4;
/* convert angles from degrees to radians */
for(i=1;i<=5;i++) ti[i] *= PI/180.0;
/* adjust extrema parameters for mean heart rate */
hrfact = sqrt(hrmean/60.0);
hrfact2 = sqrt(hrfact);
for(i=1;i<=5;i++) bi[i] *= hrfact;
ti[1]*=hrfact2; ti[2]*=hrfact; ti[3]*=1.0; ti[4]*=hrfact; ti[5]*=1.0;
/* calculate time scales */
h = 1.0/sf;
tstep = 1.0/sfecg;
printf("ECGSYN: A program for generating a realistic synthetic ECG\n"
"Copyright (c) 2003 by Patrick McSharry & Gari Clifford. All rights reserved.\n"
"See IEEE Transactions On Biomedical Engineering, 50(3), 289-294, March 2003.\n"
"Contact P. McSharry (patrick@mcsharry.net) or G. Clifford (gari@mit.edu)\n");
printf("Approximate number of heart beats: %d\n",N);
printf("ECG sampling frequency: %d Hertz\n",sfecg);
printf("Internal sampling frequency: %d Hertz\n",sf);
printf("Amplitude of additive uniformly distributed noise: %g mV\n",Anoise);
printf("Heart rate mean: %g beats per minute\n",hrmean);
printf("Heart rate std: %g beats per minute\n",hrstd);
printf("Low frequency: %g Hertz\n",flo);
printf("High frequency std: %g Hertz\n",fhistd);
printf("Low frequency std: %g Hertz\n",flostd);
printf("High frequency: %g Hertz\n",fhi);
printf("LF/HF ratio: %g\n",lfhfratio);
/* initialise seed */
rseed = -seed;
/* select the derivs to use */
derivs = derivspqrst;
/* calculate length of RR time series */
rrmean = (60/hrmean);
Nrr = (int)pow(2.0, ceil(log10(N*rrmean*sf)/log10(2.0)));
printf("Using %d = 2^%d samples for calculating RR intervals\n",
Nrr,(int)(log10(1.0*Nrr)/log10(2.0)));
/* create rrprocess with required spectrum */
rr = mallocVect(1,Nrr);
rrprocess(rr, flo, fhi, flostd, fhistd, lfhfratio, hrmean, hrstd, sf, Nrr);
vecfile("rr.dat",rr,Nrr);
/* create piecewise constant rr */
rrpc = mallocVect(1,2*Nrr);
tecg = 0.0;
i = 1;
j = 1;
while(i <= Nrr)
{
tecg += rr[j];
j = (int)rint(tecg/h);
for(k=i;k<=j;k++) rrpc[k] = rr[i];
i = j+1;
}
Nt = j;
vecfile("rrpc.dat",rrpc,Nt);
printf("Printing ECG signal to file: %s\n",outfile);
/* integrate dynamical system using fourth order Runge-Kutta*/
xt = mallocVect(1,Nt);
yt = mallocVect(1,Nt);
zt = mallocVect(1,Nt);
timev = 0.0;
for(i=1;i<=Nt;i++)
{
xt[i] = x[1];
yt[i] = x[2];
zt[i] = x[3];
drk4(x, mstate, timev, h, x, derivs);
timev += h;
}
/* downsample to ECG sampling frequency */
xts = mallocVect(1,Nt);
yts = mallocVect(1,Nt);
zts = mallocVect(1,Nt);
j=0;
for(i=1;i<=Nt;i+=q)
{
j++;
xts[j] = xt[i];
yts[j] = yt[i];
zts[j] = zt[i];
}
Nts = j;
/* do peak detection using angle */
ipeak = mallocVect(1,Nts);
detectpeaks(ipeak, xts, yts, zts, Nts);
/* scale signal to lie between -0.4 and 1.2 mV */
zmin = zts[1];
zmax = zts[1];
for(i=2;i<=Nts;i++)
{
if(zts[i] < zmin) zmin = zts[i];
else if(zts[i] > zmax) zmax = zts[i];
}
zrange = zmax-zmin;
for(i=1;i<=Nts;i++) zts[i] = (zts[i]-zmin)*(1.6)/zrange - 0.4;
/* include additive uniformly distributed measurement noise */
for(i=1;i<=Nts;i++) zts[i] += Anoise*(2.0*ran1(&rseed) - 1.0);
/* output ECG file */
fp = fopen(outfile,"w");
for(i=1;i<=Nts;i++) fprintf(fp,"%f %f %d\n",(i-1)*tstep,zts[i],(int)ipeak[i]);
fclose(fp);
printf("Finished ECG output\n");
freeVect(x,1,mstate);
freeVect(rr,1,Nrr);
freeVect(rrpc,1,2*Nrr);
freeVect(ti,1,5);
freeVect(ai,1,5);
freeVect(bi,1,5);
freeVect(xt,1,Nt);
freeVect(yt,1,Nt);
freeVect(zt,1,Nt);
freeVect(xts,1,Nt);
freeVect(yts,1,Nt);
freeVect(zts,1,Nt);
freeVect(ipeak,1,Nts);
/* END OF DORUN */
}

View File

@@ -0,0 +1,11 @@
<html>
<head><title>Index of /static/published-projects/ecgsyn/1.0.0/C/src/</title></head>
<body>
<h1>Index of /static/published-projects/ecgsyn/1.0.0/C/src/</h1><hr><pre><a href="../">../</a>
<a href="00README">00README</a> 12-Apr-2019 17:35 4672
<a href="Makefile">Makefile</a> 12-Apr-2019 17:35 426
<a href="ecgsyn.c">ecgsyn.c</a> 12-Apr-2019 17:35 20586
<a href="opt.c">opt.c</a> 12-Apr-2019 17:35 56582
<a href="opt.h">opt.h</a> 12-Apr-2019 17:35 5237
</pre><hr></body>
</html>

2581
ecgsyn-1.0.0/C/src/opt.c Normal file

File diff suppressed because it is too large Load Diff

159
ecgsyn-1.0.0/C/src/opt.h Normal file
View File

@@ -0,0 +1,159 @@
/*
opt.h version 3.17 - User Include File for options package.
OPT is a subroutine library which facilitates the convenient
input of parameters to a C or C++ program. Parameters are parsed
from a command line, with further facilities for reading options
from files, from environment strings, or from an interactive
environment. The aim of the opt package is to permit programs to
be both user- and programmer- friendly. The package attempts to
on the one hand provide a direct and relatively full-featured
input interface to the ultimate user of the program, and at the
same time impose a minimal amount of work on the programmer to
"attach" the package to his or her software. A texinfo file is
part of the distribution; or you can view the html documentation at:
http://nis-www.lanl.gov/~jt/Software/opt/opt_toc.html
Download the lastest version of the source code from:
http://nis-www.lanl.gov/~jt/Software/
opt is available to the public under the GNU General Public License:
http://www.gnu.org/copyleft/gpl.html
This SOFTWARE has been authored by an employee of the University of
California, operator of the Los Alamos National Laboratory under
Contract No. W-7405-ENG-36 with the U.S. Department of Energy. The
U.S. Government has rights to use, reproduce, and distribute this
SOFTWARE. The public may copy, distribute, prepare derivative works
and publicly display this SOFTWARE without charge, provided that this
Notice and any statement of authorship are reproduced on all
copies. Neither the Government nor the University makes any warranty,
express or implied, or assumes any liability or responsibility for the
use of this SOFTWARE. If SOFTWARE is modified to produce derivative
works, such modified SOFTWARE should be clearly marked, so as not to
confuse it with the version available from LANL.
*/
#ifndef _OPT_H
#define _OPT_H /* Signal that this header file has been included */
/*
* These are delimiter characters
*/
#define DELIM '-' /* option delimiter character */
#define ALTDELIM '/' /* alternate delimiter character */
#define OPTFROMFILE '@' /* denotes options are in a file */
#define OPTTOFILE '%' /* says to put options in a file */
#define DOCUMENT '-' /* write document to file */
#define INTERACT '$' /* Flags interactive menu */
#define HELPCH '?' /* Help character */
/*
* These are not available on command line
* But may be invoked from a file
*/
#define IGNOREEOL ';' /* Ignore until the end of line */
#define RUN '=' /* Says to just run to completion */
#define QUITCH '.' /* Quit character */
/*
* These are not available on command line or from a file
* But may be invoked from the menu
*/
#define BANG '!' /* Shell escape character */
#define ADDITIONAL_OPTS '+' /* Additional options */
typedef enum {
INT, UNSINT, LONG, CHAR,
INTLEVEL,
FLOAT, DOUBLE,
FLAG, NEGFLAG, ABSFLAG, ABSNEGFLAG,
VSTRING, CSTRING, UNDELIMV, UNDELIMC
}
opt_TYPE;
typedef int (*PFI)();
#define OPT_EXT ".opt" /* standard options file extension */
#ifndef TRUE
#define TRUE 1
#define FALSE 0
#endif
#ifndef ARGCHECK
#ifdef __TURBOC__
#define ARGCHECK
#endif /* __TURBOC__ */
#ifdef __cplusplus
#define ARGCHECK
#endif /* __cplusplus */
#endif /* ARGCHECK */
#ifdef ARGCHECK
extern int opt_register(char *,opt_TYPE,char,char *);
extern void opt_usage_set(char *);
extern void opt_title_set(char *);
extern void opt_verify_set(PFI);
extern void opt_doc_set(PFI);
extern void opt_quit_set(PFI);
extern void opt_run_set(PFI);
extern void opt_help_setf(PFI);
extern void opt_help_set(char,char *);
extern void opt_ufilter_set(PFI);
extern void opt_dfilter_set(PFI);
extern void opt_env_set(char *);
extern void opt_default_set(char *);
#if DISABLE_VARARGS
extern void opt_errmess(char *);
extern void opt_message(char *);
extern void opt_warning(char *);
extern void opt_fatal(char *);
#else
extern void opt_errmess();
extern void opt_message();
extern void opt_warning();
extern void opt_fatal();
#endif /* DISABLE_VARARGS */
extern void opt_abort_run(void);
extern int opt_begin_run(PFI);
extern int getopts(int,char **);
#else
extern int opt_register();
extern void opt_usage_set();
extern void opt_title_set();
extern void opt_verify_set();
extern void opt_doc_set();
extern void opt_quit_set();
extern void opt_run_set();
extern void opt_help_setf();
extern void opt_help_set();
extern void opt_help_set();
extern void opt_ufilter_set();
extern void opt_dfilter_set();
extern void opt_env_set();
extern void opt_default_set();
extern void opt_errmess();
extern void opt_message();
extern void opt_warning();
extern void opt_fatal();
extern void opt_abort_run();
extern int opt_begin_run();
extern int getopts();
#endif /* ARGCHECK */
/*********************************
* Macro's for registering options
*/
#define optregister(val,typ,c,str) opt_register((char *)&val,typ,c,str)
#define optrunset(r) {int r(); opt_run_set( r );}
#endif /* _OPT_H */

View File

@@ -0,0 +1,14 @@
<!--#set var="TITLE" value="ECGSYN for Windows"-->
<!--#include virtual="/head.shtml"-->
<p>
There are two versions of the <code>ecgsyn.exe</code> executable in this directory. Using the
<a href="../src/">ECGSYN source files</a>, the first version was compiled with <code>Cygwin64 2.6.0 gcc 5.4.0</code>,
and the second version with <code>MinGW-w64 gcc 4.9.2</code>, both under Windows 10.</p>
<p>
To run the Cygwin executable, you will need to have the Cygwin DLLs installed in your PATH in
order to run this program successfully. The simplest and most reliable
way to do this is to install the <a href="http://www.cygwin.com/" target="other">Cygwin development toolkit</a>.
To run the MinGW executable, you should install <a href="https://sourceforge.net/projects/mingw-w64/" target="other">MinGW-w64</a>.
</p>

Binary file not shown.

View File

@@ -0,0 +1,10 @@
<html>
<head><title>Index of /static/published-projects/ecgsyn/1.0.0/C/windows/</title></head>
<body>
<h1>Index of /static/published-projects/ecgsyn/1.0.0/C/windows/</h1><hr><pre><a href="../">../</a>
<a href="HEADER.shtml">HEADER.shtml</a> 12-Apr-2019 17:35 821
<a href="HEADER.shtml~">HEADER.shtml~</a> 12-Apr-2019 17:35 647
<a href="cygwin_ecgsyn.zip">cygwin_ecgsyn.zip</a> 12-Apr-2019 17:35 35146
<a href="mingw_ecgsyn.zip">mingw_ecgsyn.zip</a> 12-Apr-2019 17:35 34966
</pre><hr></body>
</html>

Binary file not shown.

Binary file not shown.

View File

@@ -0,0 +1,21 @@
<!--#set var="TITLE" value="ECGSYN implemented in Java"-->
<!--#include virtual="/head.shtml"-->
<p>
This directory contains Mauricio Villarroel's implementation of
<a href="../">ECGSYN</a> as a Java applet.
<p>
To run the applet in your web browser, click
<a href="ecgsyn-java.html">here</a>.
<p>
The individual Java files can be downloaded by following the links
below, or you may download all of them, together with the C and Matlab
implementations of ECGSYN, as a single gzip-compressed tar archive, <a
href="../ecgsyn.tar.gz"><tt>ecgsyn.tar.gz</tt></a>. (See <a
href="/faq.shtml#tar-gz">How can I unpack a .tar.gz archive?</a> if
you need help doing this.)
<hr>

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,64 @@
<HTML>
<HEAD>
<TITLE>ECGSYN Java Application</TITLE>
<meta http-equiv="Content-Type" content="text/html; charset=iso-8859-1"></HEAD>
<BODY bgcolor="#003366" text="#FFFFCC" link="#FFFF33" vlink="#FFCC00" leftmargin="0" topmargin="0" marginwidth="0" marginheight="0">
<table width="925" border="0" align="center" cellpadding="0" cellspacing="5">
<tr>
<td height="50" colspan="2">&nbsp;
<H3>
<HR WIDTH="100%">
</H3>
<H3 align="center"><font color="#FF9900">JAVA Application for <br>
ECGSYN: A Dynamical Model for Generating
Synthetic Electrocardiogram Signals
</font></H3>
<H3>
<HR WIDTH="100%">
</H3>
</td>
</tr>
<tr>
<td width="300" align="left" valign="top"><p><strong>ECGSYN</strong>:</p>
<p><strong>&copy;</strong> ECGSYN Copyright by Patrick E. McSharry and Gari
D. Clifford. <br>
&copy; Java code copyright by Mauricio Villarroel.<br>
<br>
For the Mathematical Model, see:</p>
<table width="100%" border="1" cellspacing="0" cellpadding="5">
<tr>
<td align="left" valign="top">IEEE Transactions On Biomedical Engineering,
50(3), 289-294, March 2003</td>
</tr>
</table>
<p> Contact: </p>
<ul>
<li><a href="mailto:patrick@mcsharry.net">Patrck McSharry</a></li>
<li><a href="mailto:gari@mit.edu">Gari Clifford</a></li>
<li><a href="mailto:mauricio.villarroel@estudiantes.ucb.edu.bo">Mauricio Villarroel</a></li>
</ul>
<p>Please read the <a href="ecgLicense.txt">Software License</a>.
</p></td>
<td align="center" valign="middle">ECG Ouput Sample image:<br><p><img src="ecgplot.PNG" alt="ECGSYN-Java Output Sample image" width="580" height="330" border="1"></p>
<p>Source code: <a href="ecgApplet.tar.gz">ecgApplet.tar.gz</a></p></td>
</tr>
<tr>
<td height="50">&nbsp;</td>
<td height="50" align="center" valign="middle">&nbsp;</td>
</tr>
<tr>
<td colspan="2" align="center" valign="top"><strong><font size="4">You can
test the ECGSYN Java Application:</font></strong></td>
</tr>
<tr>
<td colspan="2" align="center" valign="top"><applet name= "ecgApplet" code="ecgApplet.class" width=915 height=570>
</applet></td>
</tr>
</table>
<H3>&nbsp; </H3>
<P>&nbsp; </P>
<HR WIDTH="100%"><FONT SIZE=-1><I>ECGSYN Java Application<br>
Last updated on October 27, 2003</I></FONT>
<p align="justify">Return to ECGSYN <a href="http://www.maths.ox.ac.uk/~mcsharry/ecgsyn">Home page</a>.</p>
</BODY>
</HTML>

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,39 @@
/************************************************************************/
/* */
/* ECGSYN Java Application */
/* */
/* Copyright (C) 2003 Mauricio Villarroel */
/* (mauricio DOT villarroel AT estudiantes DOT ucb DOT edu DOT bo) */
/* */
/* ecgApplet.java and all its components are free software; you can */
/* redistribute them and/or modify it under the terms of the */
/* GNU General Public License as published by the Free Software */
/* Foundation; either version 2 of the License, or (at your option) */
/* any later version. */
/* */
/* This file 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. */
/* */
/************************************************************************/
/* */
/* This file was created for the ECGSYN Application. */
/* */
/* ECGSYN: A program for generating a realistic synthetic */
/* Electrocardiogram Signals. */
/* Copyright (c) 2003 by Patrick McSharry & Gari Clifford. */
/* All rights reserved. */
/* */
/* See IEEE Transactions On Biomedical Engineering, */
/* 50(3), 289-294, March 2003. */
/* Contact: */
/* P. McSharry (patrick AT mcsharry DOT net) */
/* G. Clifford (gari AT mit DOT edu) */
/* */
/************************************************************************/
/* */
/* Further updates to this software will be published on: */
/* http://www.physionet.org/ */
/* */
/************************************************************************/

Binary file not shown.

After

Width:  |  Height:  |  Size: 10 KiB

View File

@@ -0,0 +1,64 @@
<HTML>
<HEAD>
<TITLE>ECGSYN Java Application</TITLE>
<meta http-equiv="Content-Type" content="text/html; charset=iso-8859-1"></HEAD>
<BODY bgcolor="#003366" text="#FFFFCC" link="#FFFF33" vlink="#FFCC00" leftmargin="0" topmargin="0" marginwidth="0" marginheight="0">
<table width="925" border="0" align="center" cellpadding="0" cellspacing="5">
<tr>
<td height="50" colspan="2">&nbsp;
<H3>
<HR WIDTH="100%">
</H3>
<H3 align="center"><font color="#FF9900">JAVA Application for <br>
ECGSYN: A Dynamical Model for Generating
Synthetic Electrocardiogram Signals
</font></H3>
<H3>
<HR WIDTH="100%">
</H3>
</td>
</tr>
<tr>
<td width="300" align="left" valign="top"><p><strong>ECGSYN</strong>:</p>
<p><strong>&copy;</strong> ECGSYN Copyright by Patrick E. McSharry and Gari
D. Clifford. <br>
&copy; Java code copyright by Mauricio Villarroel.<br>
<br>
For the Mathematical Model, see:</p>
<table width="100%" border="1" cellspacing="0" cellpadding="5">
<tr>
<td align="left" valign="top">IEEE Transactions On Biomedical Engineering,
50(3), 289-294, March 2003</td>
</tr>
</table>
<p> Contact: </p>
<ul>
<li><a href="mailto:patrick@mcsharry.net">Patrck McSharry</a></li>
<li><a href="mailto:gari@mit.edu">Gari Clifford</a></li>
<li><a href="mailto:mauricio.villarroel@estudiantes.ucb.edu.bo">Mauricio Villarroel</a></li>
</ul>
<p>Please read the <a href="ecgLicense.txt">Software License</a>.
</p></td>
<td align="center" valign="middle">ECG Ouput Sample image:<br><p><img src="ecgplot.PNG" alt="ECGSYN-Java Output Sample image" width="580" height="330" border="1"></p>
<p>Source code: <a href="../ecgsyn.tar.gz">ecgsyn.tar.gz</a></p></td>
</tr>
<tr>
<td height="50">&nbsp;</td>
<td height="50" align="center" valign="middle">&nbsp;</td>
</tr>
<tr>
<td colspan="2" align="center" valign="top"><strong><font size="4">You can
test the ECGSYN Java Application:</font></strong></td>
</tr>
<tr>
<td colspan="2" align="center" valign="top"><applet name= "ecgApplet" code="ecgApplet.class" width=915 height=570>
</applet></td>
</tr>
</table>
<H3>&nbsp; </H3>
<P>&nbsp; </P>
<HR WIDTH="100%"><FONT SIZE=-1><I>ECGSYN Java Application<br>
Last updated on October 27, 2003</I></FONT>
<p align="justify">Return to ECGSYN <a href="../">Home page</a>.</p>
</BODY>
</HTML>

View File

@@ -0,0 +1,40 @@
<html>
<head><title>Index of /static/published-projects/ecgsyn/1.0.0/Java/</title></head>
<body>
<h1>Index of /static/published-projects/ecgsyn/1.0.0/Java/</h1><hr><pre><a href="../">../</a>
<a href="FormatNumber.class">FormatNumber.class</a> 12-Apr-2019 17:35 1020
<a href="HEADER.shtml">HEADER.shtml</a> 12-Apr-2019 17:35 673
<a href="ecgApplet%241.class">ecgApplet$1.class</a> 12-Apr-2019 17:35 989
<a href="ecgApplet%2410.class">ecgApplet$10.class</a> 12-Apr-2019 17:35 671
<a href="ecgApplet%2411.class">ecgApplet$11.class</a> 12-Apr-2019 17:35 671
<a href="ecgApplet%2412.class">ecgApplet$12.class</a> 12-Apr-2019 17:35 671
<a href="ecgApplet%2413.class">ecgApplet$13.class</a> 12-Apr-2019 17:35 671
<a href="ecgApplet%2414.class">ecgApplet$14.class</a> 12-Apr-2019 17:35 671
<a href="ecgApplet%2415.class">ecgApplet$15.class</a> 12-Apr-2019 17:35 671
<a href="ecgApplet%2416.class">ecgApplet$16.class</a> 12-Apr-2019 17:35 672
<a href="ecgApplet%2417.class">ecgApplet$17.class</a> 12-Apr-2019 17:35 672
<a href="ecgApplet%2418.class">ecgApplet$18.class</a> 12-Apr-2019 17:35 672
<a href="ecgApplet%242.class">ecgApplet$2.class</a> 12-Apr-2019 17:35 669
<a href="ecgApplet%243.class">ecgApplet$3.class</a> 12-Apr-2019 17:35 669
<a href="ecgApplet%244.class">ecgApplet$4.class</a> 12-Apr-2019 17:35 669
<a href="ecgApplet%245.class">ecgApplet$5.class</a> 12-Apr-2019 17:35 941
<a href="ecgApplet%246.class">ecgApplet$6.class</a> 12-Apr-2019 17:35 941
<a href="ecgApplet%247.class">ecgApplet$7.class</a> 12-Apr-2019 17:35 941
<a href="ecgApplet%248.class">ecgApplet$8.class</a> 12-Apr-2019 17:35 1108
<a href="ecgApplet%249.class">ecgApplet$9.class</a> 12-Apr-2019 17:35 669
<a href="ecgApplet%24ECGAnimate.class">ecgApplet$ECGAnimate.class</a> 12-Apr-2019 17:35 2716
<a href="ecgApplet%24doubleVerifier.class">ecgApplet$doubleVerifier.class</a> 12-Apr-2019 17:35 1485
<a href="ecgApplet%24ecgCalc.class">ecgApplet$ecgCalc.class</a> 12-Apr-2019 17:35 13305
<a href="ecgApplet%24ecgPanel.class">ecgApplet$ecgPanel.class</a> 12-Apr-2019 17:35 2618
<a href="ecgApplet%24integerVerifier.class">ecgApplet$integerVerifier.class</a> 12-Apr-2019 17:35 1489
<a href="ecgApplet%24sfVerifier.class">ecgApplet$sfVerifier.class</a> 12-Apr-2019 17:35 1825
<a href="ecgApplet%24sfecgVerifier.class">ecgApplet$sfecgVerifier.class</a> 12-Apr-2019 17:35 1615
<a href="ecgApplet.class">ecgApplet.class</a> 12-Apr-2019 17:35 40664
<a href="ecgApplet.form">ecgApplet.form</a> 12-Apr-2019 17:35 79509
<a href="ecgApplet.html">ecgApplet.html</a> 12-Apr-2019 17:35 2583
<a href="ecgApplet.java">ecgApplet.java</a> 12-Apr-2019 17:35 109386
<a href="ecgLicense.txt">ecgLicense.txt</a> 12-Apr-2019 17:35 2964
<a href="ecgplot.PNG">ecgplot.PNG</a> 12-Apr-2019 17:35 10273
<a href="ecgsyn-java.html">ecgsyn-java.html</a> 12-Apr-2019 17:35 2541
</pre><hr></body>
</html>

View File

@@ -0,0 +1,9 @@
<!--#set var="TITLE" value="ECGSYN for Matlab/Octave"-->
<!--#include virtual="/head.shtml"-->
<p>
This directory contains <a href="../">ECGSYN</a> implemented in Matlab
m-code.
<hr>

View File

@@ -0,0 +1,52 @@
function dxdt = derivsecgsyn(t,x,flag,rr,sfint,ti,ai,bi)
% dxdt = derivsecgsyn(t,x,flag,rr,sampfreq,ti,ai,bi)
% ODE file for generating the synthetic ECG
% This file provides dxdt = F(t,x) taking input paramters:
% rr: rr process
% sfint: Internal sampling frequency [Hertz]
% Order of extrema: [P Q R S T]
% ti = angles of extrema [radians]
% ai = z-position of extrema
% bi = Gaussian width of peaks
% Copyright (c) 2003 by Patrick McSharry & Gari Clifford, All Rights Reserved
% See IEEE Transactions On Biomedical Engineering, 50(3), 289-294, March 2003.
% Contact P. McSharry (patrick AT mcsharry DOT net) or
% G.D. Clifford (gari AT mit DOT edu)
% This program is free software; you can redistribute it and/or modify
% it under the terms of the GNU General Public License as published by
% the Free Software Foundation; either version 2 of the License, or
% (at your option) any later version.
%
% 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 General Public License
% along with this program; if not, write to the Free Software
% Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
%
% ecgsyn.m and its dependents are freely availble from Physionet -
% http://www.physionet.org/ - please report any bugs to the authors above.
xi = cos(ti);
yi = sin(ti);
ta = atan2(x(2),x(1));
r0 = 1;
a0 = 1.0 - sqrt(x(1)^2 + x(2)^2)/r0;
ip = 1+floor(t*sfint);
w0 = 2*pi/rr(ip);
fresp = 0.25;
zbase = 0.005*sin(2*pi*fresp*t);
dx1dt = a0*x(1) - w0*x(2);
dx2dt = a0*x(2) + w0*x(1);
dti = rem(ta - ti, 2*pi);
dx3dt = - sum(ai.*dti.*exp(-0.5*(dti./bi).^2)) - 1.0*(x(3) - zbase);
dxdt = [dx1dt; dx2dt; dx3dt];

View File

@@ -0,0 +1,240 @@
function [s, ipeaks] = ecgsyn(sfecg,N,Anoise,hrmean,hrstd,lfhfratio,sfint,ti,ai,bi)
% [s, ipeaks] = ecgsyn(sfecg,N,Anoise,hrmean,hrstd,lfhfratio,sfint,ti,ai,bi)
% Produces synthetic ECG with the following outputs:
% s: ECG (mV)
% ipeaks: labels for PQRST peaks: P(1), Q(2), R(3), S(4), T(5)
% A zero lablel is output otherwise ... use R=find(ipeaks==3);
% to find the R peaks s(R), etc.
%
% Operation uses the following parameters (default values in []s):
% sfecg: ECG sampling frequency [256 Hertz]
% N: approximate number of heart beats [256]
% Anoise: Additive uniformly distributed measurement noise [0 mV]
% hrmean: Mean heart rate [60 beats per minute]
% hrstd: Standard deviation of heart rate [1 beat per minute]
% lfhfratio: LF/HF ratio [0.5]
% sfint: Internal sampling frequency [256 Hertz]
% Order of extrema: [P Q R S T]
% ti = angles of extrema [-70 -15 0 15 100] degrees
% ai = z-position of extrema [1.2 -5 30 -7.5 0.75]
% bi = Gaussian width of peaks [0.25 0.1 0.1 0.1 0.4]
% Copyright (c) 2003 by Patrick McSharry & Gari Clifford, All Rights Reserved
% See IEEE Transactions On Biomedical Engineering, 50(3), 289-294, March 2003.
% Contact P. McSharry (patrick@mcsharry.net) or G. Clifford (gari@mit.edu)
% This program is free software; you can redistribute it and/or modify
% it under the terms of the GNU General Public License as published by
% the Free Software Foundation; either version 2 of the License, or
% (at your option) any later version.
%
% 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 General Public License
% along with this program; if not, write to the Free Software
% Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
%
% ecgsyn.m and its dependents are freely availble from Physionet -
% http://www.physionet.org/ - please report any bugs to the authors above.
% set parameter default values
if nargin < 1
sfecg = 256;
end
if nargin < 2
N = 256;
end
if nargin < 3
Anoise = 0;
end
if nargin < 4
hrmean = 60;
end
if nargin < 5
hrstd = 1;
end
if nargin < 6
lfhfratio = 0.5;
end
if nargin < 7
sfint = 512;
end
if nargin <8
% P Q R S T
ti = [-70 -15 0 15 100];
end
% convert to radians
ti = ti*pi/180;
if nargin <9 % z position of attractor
% P Q R S T
ai = [1.2 -5 30 -7.5 0.75];
end
if nargin <10 % Gaussian width of each attractor
% P Q R S T
bi = [0.25 0.1 0.1 0.1 0.4];
end
% adjust extrema parameters for mean heart rate
hrfact = sqrt(hrmean/60);
hrfact2 = sqrt(hrfact);
bi = hrfact*bi;
ti = [hrfact2 hrfact 1 hrfact hrfact2].*ti;
% check that sfint is an integer multiple of sfecg
q = round(sfint/sfecg);
qd = sfint/sfecg;
if q ~= qd
error(['Internal sampling frequency (sfint) must be an integer multiple ' ...
'of the ECG sampling frequency (sfecg). Your current choices are: ' ...
'sfecg = ' int2str(sfecg) ' and sfint = ' int2str(sfint) '.']);
end
% define frequency parameters for rr process
% flo and fhi correspond to the Mayer waves and respiratory rate respectively
flo = 0.1;
fhi = 0.25;
flostd = 0.01;
fhistd = 0.01;
fid = 1;
fprintf(fid,'ECG sampled at %d Hz\n',sfecg);
fprintf(fid,'Approximate number of heart beats: %d\n',N);
fprintf(fid,'Measurement noise amplitude: %d \n',Anoise);
fprintf(fid,'Heart rate mean: %d bpm\n',hrmean);
fprintf(fid,'Heart rate std: %d bpm\n',hrstd);
fprintf(fid,'LF/HF ratio: %g\n',lfhfratio);
fprintf(fid,'Internal sampling frequency: %g\n',sfint);
fprintf(fid,' P Q R S T\n');
fprintf(fid,'ti = [%g %g %g %g %g] radians\n',ti(1),ti(2),ti(3),ti(4),ti(5));
fprintf(fid,'ai = [%g %g %g %g %g]\n',ai(1),ai(2),ai(3),ai(4),ai(5));
fprintf(fid,'bi = [%g %g %g %g %g]\n',bi(1),bi(2),bi(3),bi(4),bi(5));
% calculate time scales for rr and total output
sampfreqrr = 1;
trr = 1/sampfreqrr;
tstep = 1/sfecg;
rrmean = (60/hrmean);
Nrr = 2^(ceil(log2(N*rrmean/trr)));
% compute rr process
rr0 = rrprocess(flo,fhi,flostd,fhistd,lfhfratio,hrmean,hrstd,sampfreqrr,Nrr);
% upsample rr time series from 1 Hz to sfint Hz
rr = interp(rr0,sfint);
% make the rrn time series
dt = 1/sfint;
rrn = zeros(length(rr),1);
tecg=0;
i = 1;
while i <= length(rr)
tecg = tecg+rr(i);
ip = round(tecg/dt);
rrn(i:ip) = rr(i);
i = ip+1;
end
Nt = ip;
% integrate system using fourth order Runge-Kutta
fprintf(fid,'Integrating dynamical system\n');
x0 = [1,0,0.04];
Tspan = [0:dt:(Nt-1)*dt];
[T,X0] = ode45('derivsecgsyn',Tspan,x0,[],rrn,sfint,ti,ai,bi);
% downsample to required sfecg
X = X0(1:q:end,:);
% extract R-peaks times
ipeaks = detectpeaks(X, ti, sfecg);
% Scale signal to lie between -0.4 and 1.2 mV
z = X(:,3);
zmin = min(z);
zmax = max(z);
zrange = zmax - zmin;
z = (z - zmin)*(1.6)/zrange -0.4;
% include additive uniformly distributed measurement noise
eta = 2*rand(length(z),1)-1;
s = z + Anoise*eta;
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
function rr = rrprocess(flo, fhi, flostd, fhistd, lfhfratio, hrmean, hrstd, sfrr, n)
w1 = 2*pi*flo;
w2 = 2*pi*fhi;
c1 = 2*pi*flostd;
c2 = 2*pi*fhistd;
sig2 = 1;
sig1 = lfhfratio;
rrmean = 60/hrmean;
rrstd = 60*hrstd/(hrmean*hrmean);
df = sfrr/n;
w = [0:n-1]'*2*pi*df;
dw1 = w-w1;
dw2 = w-w2;
Hw1 = sig1*exp(-0.5*(dw1/c1).^2)/sqrt(2*pi*c1^2);
Hw2 = sig2*exp(-0.5*(dw2/c2).^2)/sqrt(2*pi*c2^2);
Hw = Hw1 + Hw2;
Hw0 = [Hw(1:n/2+1); Hw(n/2:-1:2)];
Sw = (sfrr/2)*sqrt(Hw0);
ph0 = 2*pi*rand(n/2-1,1);
ph = [ 0; ph0; 0; -flipud(ph0) ];
SwC = Sw .* exp(j*ph);
x = (1/n)*real(ifft(SwC));
xstd = std(x);
ratio = rrstd/xstd;
rr = rrmean + x*ratio;
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
function ind = detectpeaks(X, thetap, sfecg)
N = length(X);
irpeaks = zeros(N,1);
theta = atan2(X(:,2),X(:,1));
ind0 = zeros(N,1);
for i=1:N-1
a = ( (theta(i) <= thetap) & (thetap <= theta(i+1)) );
j = find(a==1);
if ~isempty(j)
d1 = thetap(j) - theta(i);
d2 = theta(i+1) - thetap(j);
if d1 < d2
ind0(i) = j;
else
ind0(i+1) = j;
end
end
end
d = ceil(sfecg/64);
d = max([2 d])
ind = zeros(N,1);
z = X(:,3);
zmin = min(z);
zmax = max(z);
zext = [zmin zmax zmin zmax zmin];
sext = [1 -1 1 -1 1];
for i=1:5
clear ind1 Z k vmax imax iext;
ind1 = find(ind0==i);
n = length(ind1);
Z = ones(n,2*d+1)*zext(i)*sext(i);
for j=-d:d
k = find( (1 <= ind1+j) & (ind1+j <= N) );
Z(k,d+j+1) = z(ind1(k)+j)*sext(i);
end
[vmax, ivmax] = max(Z,[],2);
iext = ind1 + ivmax-d-1;
ind(iext) = i;
end

View File

@@ -0,0 +1,9 @@
<html>
<head><title>Index of /static/published-projects/ecgsyn/1.0.0/Matlab/</title></head>
<body>
<h1>Index of /static/published-projects/ecgsyn/1.0.0/Matlab/</h1><hr><pre><a href="../">../</a>
<a href="HEADER.shtml">HEADER.shtml</a> 12-Apr-2019 17:35 186
<a href="derivsecgsyn.m">derivsecgsyn.m</a> 12-Apr-2019 17:35 1911
<a href="ecgsyn.m">ecgsyn.m</a> 12-Apr-2019 17:35 6795
</pre><hr></body>
</html>

BIN
ecgsyn-1.0.0/ecgsyn.tar.gz Normal file

Binary file not shown.

12
ecgsyn-1.0.0/index.html Normal file
View File

@@ -0,0 +1,12 @@
<html>
<head><title>Index of /static/published-projects/ecgsyn/1.0.0/</title></head>
<body>
<h1>Index of /static/published-projects/ecgsyn/1.0.0/</h1><hr><pre><a href="../">../</a>
<a href="C/">C/</a> 12-Apr-2019 17:35 -
<a href="Java/">Java/</a> 12-Apr-2019 17:35 -
<a href="Matlab/">Matlab/</a> 12-Apr-2019 17:35 -
<a href="paper/">paper/</a> 12-Apr-2019 17:35 -
<a href="sample-output/">sample-output/</a> 12-Apr-2019 17:35 -
<a href="ecgsyn.tar.gz">ecgsyn.tar.gz</a> 12-Apr-2019 17:35 165286
</pre><hr></body>
</html>

View File

@@ -0,0 +1,30 @@
/* Century Schoolbook font is very similar to Computer Modern Math: cmmi */
.MATH { font-family: "Century Schoolbook", serif; }
.MATH I { font-family: "Century Schoolbook", serif; font-style: italic }
.BOLDMATH { font-family: "Century Schoolbook", serif; font-weight: bold }
/* implement both fixed-size and relative sizes */
SMALL.XTINY { font-size : xx-small }
SMALL.TINY { font-size : x-small }
SMALL.SCRIPTSIZE { font-size : smaller }
SMALL.FOOTNOTESIZE { font-size : small }
SMALL.SMALL { }
BIG.LARGE { }
BIG.XLARGE { font-size : large }
BIG.XXLARGE { font-size : x-large }
BIG.HUGE { font-size : larger }
BIG.XHUGE { font-size : xx-large }
/* heading styles */
H1 { }
H2 { }
H3 { }
H4 { }
H5 { }
/* mathematics styles */
DIV.displaymath { } /* math displays */
TD.eqno { } /* equation-number cells */
/* document-specific styles come next */

View File

@@ -0,0 +1,79 @@
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 3.2 Final//EN">
<!--Converted with jLaTeX2HTML 2002 (1.62) JA patch-1.4
patched version by: Kenshi Muto, Debian Project.
LaTeX2HTML 2002 (1.62),
original version by: Nikos Drakos, CBLU, University of Leeds
* revised and updated by: Marcus Hennecke, Ross Moore, Herb Swan
* with significant contributions from:
Jens Lippmann, Marek Rouchal, Martin Wilck and others -->
<HTML>
<HEAD>
<TITLE>Footnotes</TITLE>
<META NAME="description" CONTENT="Footnotes">
<META NAME="keywords" CONTENT="ecgpqrst">
<META NAME="resource-type" CONTENT="document">
<META NAME="distribution" CONTENT="global">
<META HTTP-EQUIV="Content-Type" CONTENT="text/html; charset=iso-8859-1">
<META NAME="Generator" CONTENT="jLaTeX2HTML v2002 JA patch-1.4">
<META HTTP-EQUIV="Content-Style-Type" CONTENT="text/css">
<LINK REL="STYLESHEET" HREF="ecgpqrst.css">
<LINK REL="previous" HREF="node8.html">
<LINK REL="up" HREF="index.shtml">
</HEAD>
<BODY >
<DL>
<DT><A NAME="foot31">...<IMG
WIDTH="21" HEIGHT="17" ALIGN="BOTTOM" BORDER="0"
SRC="img6.png"
ALT="$^{2,3}$"></A><A
HREF="index.shtml#tex2html1"><SUP><IMG ALIGN="BOTTOM" BORDER="1" ALT="[*]"
SRC="file:/usr/share/latex2html/icons/footnote.png"></SUP></A></DT>
<DD><IMG
WIDTH="11" HEIGHT="17" ALIGN="BOTTOM" BORDER="0"
SRC="img5.png"
ALT="$^1$">Department of Engineering Science,
University of Oxford, Oxford OX1 3PJ.
<PRE>.
</PRE>
</DD>
<DT><A NAME="foot32">...</A><A
HREF="index.shtml#tex2html2"><SUP><IMG ALIGN="BOTTOM" BORDER="1" ALT="[*]"
SRC="file:/usr/share/latex2html/icons/footnote.png"></SUP></A></DT>
<DD><IMG
WIDTH="11" HEIGHT="17" ALIGN="BOTTOM" BORDER="0"
SRC="img7.png"
ALT="$^2$">Mathematical Institute, University of Oxford, Oxford OX1 3LB.
<PRE>.
</PRE>
</DD>
<DT><A NAME="foot33">...</A><A
HREF="index.shtml#tex2html3"><SUP><IMG ALIGN="BOTTOM" BORDER="1" ALT="[*]"
SRC="file:/usr/share/latex2html/icons/footnote.png"></SUP></A></DT>
<DD><IMG
WIDTH="11" HEIGHT="17" ALIGN="BOTTOM" BORDER="0"
SRC="img8.png"
ALT="$^3$">Centre for the Analysis of Time Series, London School
of Economics, London WC2A 2AE.
<PRE>.
</PRE>
</DD>
<DT><A NAME="foot34">...</A><A
HREF="index.shtml#tex2html4"><SUP><IMG ALIGN="BOTTOM" BORDER="1" ALT="[*]"
SRC="file:/usr/share/latex2html/icons/footnote.png"></SUP></A></DT>
<DD>E-mail: mcsharry@robots.ox.ac.uk
<PRE>.
</PRE>
</DD>
</DL>
</BODY>
</HTML>

BIN
ecgsyn-1.0.0/paper/img1.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 446 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.8 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 393 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 182 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 274 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 293 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 289 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 275 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 264 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 217 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 150 B

BIN
ecgsyn-1.0.0/paper/img2.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 392 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 426 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 232 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 445 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 202 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.7 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 655 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 813 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 662 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 214 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 236 B

BIN
ecgsyn-1.0.0/paper/img3.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 553 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 260 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 823 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 394 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 472 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 256 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 257 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 237 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 249 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 590 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 296 B

BIN
ecgsyn-1.0.0/paper/img4.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 220 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 320 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 287 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 274 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.1 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.3 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.7 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 369 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 318 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 275 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 268 B

Some files were not shown because too many files have changed in this diff Show More