Dydx swap Is a practical guide to trading, fees, and token swap basics

Dydx swap Is a user search term for exchanging crypto assets through the dYdX trading ecosystem, where users commonly compare tokens, wallet setup, order types, fees, liquidity, and execution risk before making a trade. In simple terms, it means preparing to swap or trade digital assets with a focus on price, slippage, network costs, platform rules, and personal custody. Users should verify current details with official sources before connecting a wallet or placing any transaction.

Dydx swap often appears in searches from people who want a plain-English explanation before they interact with a decentralized finance platform. The phrase can refer to a simple token conversion, but it may also point to broader trading activity on dYdX, including perpetual markets, collateral management, deposits, withdrawals, and order execution. Because crypto markets move quickly, the safest approach is to treat every Dydx swap as a transaction that deserves a careful quote review and a clear understanding of downside risk.

Dydx swap is not a guarantee of a better price, instant settlement, or lower cost than every other exchange route. It is a workflow: choose a market, review liquidity, understand how the trade will execute, approve only what is necessary, and confirm the result in your wallet or account history. This guide explains the basics without assuming that a user already understands decentralized exchanges, margin systems, derivatives, or token approvals.

What is Dydx swap?

Dydx swap is best understood as the process a user follows when exchanging or trading digital assets through the dYdX environment. Depending on the current product design and supported markets, that activity may look different from a basic wallet-to-wallet token swap on an automated market maker. dYdX is widely associated with decentralized trading infrastructure, order books, perpetual contracts, and self-directed crypto market access, so a Dydx swap search usually involves more than pressing one trade button.

Dydx swap can include spot-like thinking, such as comparing one asset against another, but users should pay attention to whether they are interacting with a simple token exchange or a derivatives market. A perpetual contract is not the same as owning the underlying token. It can track price exposure, use collateral, and involve funding rates or liquidation risk. That distinction matters because a Dydx swap entered as a casual token trade may behave differently if the product being used is a leveraged instrument.

For a new user, Dydx swap should begin with definitions. A wallet is the tool used to connect and authorize activity. Collateral is the value used to support positions. Liquidity is the available market depth near the current price. Slippage is the difference between the quoted price and the executed price. Fees are the explicit costs charged by the venue or network. These concepts are basic, but they shape the real result of any Dydx swap.

How does Dydx swap work in practice?

Dydx swap generally starts when a user connects a compatible wallet or account, chooses a market, and reviews the available trading interface. Instead of assuming the displayed price is final, the user should check the order type, the estimated execution price, and whether the trade will fill immediately or rest on an order book. A market order prioritizes execution, while a limit order sets a maximum or minimum acceptable price.

Dydx swap activity may involve deposits and withdrawals rather than a direct one-click conversion from any token to any other token. In practice, a user may first need to bring supported collateral into the trading environment, then use that collateral to open, close, or adjust exposure. That is why comparing the full workflow matters. The visible trading fee is only one part of the cost; bridging, gas, spread, funding, and withdrawal fees may also affect the final outcome.

Dydx swap decisions are also shaped by settlement and confirmation. A wallet approval or signature does not always mean the economic trade has completed in the way the user expected. The user should confirm the filled amount, average price, fee, and remaining balances after execution. If anything looks unfamiliar, it is better to stop and inspect the account history than to repeat the trade and compound a mistake.

Why do traders use Dydx swap workflows?

Dydx swap workflows are attractive to some crypto users because they combine market access with more control over execution. Traders who understand order books may prefer choosing limit prices instead of accepting a simple instant quote. They may also value transparent position history, market depth, and the ability to manage entries and exits more precisely than with a basic swap widget.

Dydx swap activity can support several common use cases. A user might adjust exposure to a major crypto asset, move from one market view to another, test a small transaction before a larger one, or close a position after a price move. Active traders may also compare dYdX with centralized exchanges, wallet-native swaps, aggregators, and automated market makers to decide which route provides the best combination of cost, speed, liquidity, and control.

The protocol environment may appeal to users who want self-directed DeFi access, but self-directed also means self-responsible. Dydx swap users must understand that there may be no customer-service reversal for a mistaken order, wrong wallet connection, or misunderstood contract. That is why disciplined trade sizing, official-source verification, and careful wallet hygiene are not optional details.

Fees and costs of Dydx swap

Dydx swap cost is not a single number. A user may encounter trading fees, spread, slippage, funding payments, gas fees, bridging costs, withdrawal charges, or other network-related expenses depending on how the trade is routed and what product is used. Fee schedules can change, so users should verify the current fee table and market rules through official dYdX materials before trading.

Dydx swap fees should be reviewed together with execution quality. A trade with a low explicit fee can still be expensive if the market is thin, the order is too large, or the price moves while the transaction is pending. In an order book, liquidity near the top of book matters. In a swap-style quote, price impact matters. Both are ways of asking the same practical question: how much value will the user actually receive after the trade completes?

Cost factor Why it matters
Trading fee The platform-level charge applied to the trade or order execution.
Spread The gap between buying and selling prices, which can widen in volatile markets.
Slippage The difference between expected and executed price, especially on larger orders.
Network cost Gas, bridge, or settlement expenses that may apply outside the quoted trade fee.
Funding or position cost Costs that may apply when using perpetual markets rather than simple token ownership.

Dydx swap comparisons should include the total path from wallet balance to final position. If a user deposits collateral, trades, then withdraws, the relevant cost is the complete round trip. That broader view prevents a common mistake: focusing only on the fee displayed next to the trade button while ignoring the costs that happen before and after the order.

How to get started with Dydx swap

Dydx swap preparation should be slow and deliberate, especially for a first transaction. Users should begin with a small amount, confirm they are using the intended official interface, and read the prompts presented by the wallet. A signature request may be harmless login authentication, or it may authorize a meaningful action. The text of the prompt matters, and users should avoid approving anything they do not understand.

  1. Confirm the correct official dYdX destination and avoid sponsored or copied links.
  2. Connect a wallet only after checking the URL, network, and account.
  3. Review the market, order type, collateral, size, leverage, and estimated fees.
  4. Place a small test transaction before considering a larger Dydx swap.
  5. Verify the filled amount, average price, position status, and remaining balance.

Dydx swap users should also create a personal routine. Check whether the trade is a spot-style exchange or a perpetual position. Confirm whether leverage is enabled. Look for liquidation price if margin is involved. Review open orders after trading, because an unfilled limit order can remain active and execute later when market conditions change.

In practice, Dydx swap confidence comes from repetition with small amounts and careful review. The goal is not to predict every price move; it is to understand what the interface is asking, what the wallet is authorizing, and what financial exposure will exist after the action is complete.

Benefits of Dydx swap for experienced users

Dydx swap may offer meaningful advantages for users who already understand crypto market structure. Order-book trading can provide more precise entries and exits than a basic instant swap. Advanced order types can help users avoid accepting a poor quote during volatile periods. Transparent market data can also make it easier to see whether a trade size is reasonable for the available liquidity.

Dydx swap workflows can be useful for traders who want control over price and timing. A limit order lets a user choose the price at which they are willing to buy or sell. A market order may be appropriate when speed matters more than price precision. Some users also value the ability to manage positions without relying on a traditional custodial exchange account, although wallet security then becomes even more important.

Dydx swap is not automatically better for every user. Someone who only wants a simple token conversion may prefer a wallet swap or aggregator if it supports the desired asset and provides a clearer quote. Someone who wants derivatives exposure must understand collateral, funding, and liquidation. The benefit depends on the user’s goal, skill level, asset, size, and risk tolerance.

Dydx swap trading screen with wallet and charts

Risks and safety checks before a Dydx swap

Dydx swap risk starts with market risk. Crypto prices can move sharply, liquidity can change, and leveraged products can create losses greater than a user expected. No interface can remove the need to understand what is being traded. Users should never treat a Dydx swap as financial advice, and they should avoid trading with funds they cannot afford to lose.

Dydx swap safety also depends on wallet security. Phishing pages, fake support accounts, malicious token approvals, copied interfaces, and urgent messages are common threats across DeFi. A user should type or bookmark the intended destination, avoid links from strangers, use hardware-wallet practices where appropriate, and revoke unnecessary approvals when they are no longer needed.

Dydx swap users should check more than price before confirming. They should review the asset symbol, chain or network, account address, order size, collateral impact, leverage, liquidation level, fees, and whether the order is immediate or pending. A single incorrect setting can turn an ordinary trade into a much riskier position.

Dydx swap safety is also about emotional control. Fast markets encourage rushed decisions. A user who feels pressured by a sudden price move, a social media claim, or a fear of missing out is more likely to skip checks. Slowing down is not a trading strategy, but it is a practical defense against avoidable errors.

Dydx swap compared with other crypto exchange routes

Dydx swap is only one possible route for crypto trading. A centralized exchange may offer fiat onboarding, customer support, and familiar account tools, but it usually requires custody and identity checks. A decentralized automated market maker may provide broad token access, but large orders can face price impact. A swap aggregator may compare routes across liquidity sources, but users still need to inspect approvals, quotes, and risks.

Dydx swap may be most relevant when a user wants dYdX-style market access, order control, or perpetual exposure. It may be less relevant when the user simply wants to buy a small amount of a token that is not supported, move between niche assets, or avoid the complexity of collateralized trading. The right choice depends on the trade, not on a slogan.

For a fair comparison, Dydx swap should be evaluated against alternatives using the same inputs: trade size, asset pair, expected price, total fees, execution time, custody model, and risk. A user comparing two routes should ask what happens if the market moves, if the transaction fails, if the order only partially fills, or if a withdrawal is delayed.

Common mistakes when using Dydx swap

Dydx swap mistakes often come from assuming every crypto interface works the same way. A user familiar with a simple token swap may overlook order types, collateral, or liquidation settings. Another user may see a low fee and ignore spread. Someone else may place a limit order, forget it is open, and later discover it executed when the market returned to that price.

Dydx swap errors can also happen during setup. Connecting the wrong wallet, using the wrong network, misunderstanding a deposit step, or approving too much token access can create unnecessary risk. Before taking action, users should confirm account identity, expected balances, and the exact asset they intend to use. Similar ticker symbols and wrapped assets can be confusing, especially for people moving across multiple chains.

Dydx swap planning should include an exit. A trader should know how to close a position, cancel an open order, withdraw funds, and review transaction history before the trade begins. Learning those steps during market stress is avoidable. A small practice trade can teach more than a large rushed trade.

Is Dydx swap right for your trading needs?

Dydx swap may be appropriate for users who understand decentralized trading, are comfortable with wallets, and want more control over order execution. It may not be appropriate for users who need beginner-level simplicity, guaranteed support, or protection from market volatility. The most important question is not whether Dydx swap is popular, but whether the user understands the product, cost, and risk well enough to make an informed decision.

Dydx swap should be approached as a careful financial action, not a casual website click. Read the current official documentation, verify the live fee schedule, test with small amounts, and keep personal security practices strict. Crypto trading can be useful for some strategies, but it can also lead to rapid losses. A thoughtful Dydx swap process is one that prioritizes verification, risk limits, and clear records over speed.

Reader rating: 4.9 / 5 based on 945 ratings

Questions and Answers

What does Dydx swap mean?

Dydx swap usually means exchanging or trading crypto assets through the dYdX ecosystem or researching how that process works. Depending on the current product and market, it may involve deposits, order-book trading, collateral, perpetual contracts, fees, and withdrawals rather than a simple one-click token conversion. Users should verify current details with official dYdX sources before connecting a wallet or placing a trade.

Is Dydx swap the same as a regular token swap?

Dydx swap is not always the same as a regular wallet token swap. A basic swap often converts one token into another through automated liquidity. dYdX is commonly associated with trading infrastructure, order books, and perpetual markets, so users may be managing price exposure rather than simply receiving a new token. Always check the product type, order details, and risk before confirming.

What fees should I check before using Dydx swap?

Before using Dydx swap, review trading fees, spread, slippage, network costs, bridging or deposit costs, withdrawal costs, and any funding or position-related costs if perpetual contracts are involved. The visible trade fee may not represent the full cost of the workflow. Fee schedules and market rules can change, so users should confirm the latest information through official sources.

Is Dydx swap safe for beginners?

Dydx swap may be complex for beginners because it can involve wallets, signatures, collateral, order types, and market risk. Safety depends on the user understanding the interface, verifying the official destination, starting with small amounts, and reading every wallet prompt. It is not risk-free, and crypto markets can move quickly. Beginners should learn the workflow before committing meaningful funds.

Can I lose money using Dydx swap?

Yes. Users can lose money from price movement, slippage, fees, poor execution, leverage, liquidation, mistaken orders, phishing, or wallet-security failures. If perpetual or margin products are involved, losses can happen faster than with a simple token purchase. Dydx swap should not be treated as financial advice or a guaranteed outcome. Trade only after understanding the specific risks.

How should I compare Dydx swap with other exchanges?

Compare Dydx swap with other routes by checking total cost, supported assets, liquidity, execution quality, custody model, order controls, and ease of withdrawal. A centralized exchange may be simpler for some users, while an automated market maker or aggregator may be better for certain token pairs. The best route depends on trade size, asset, timing, and personal risk tolerance.

What should I do before my first Dydx swap?

Before a first Dydx swap, verify the official interface, connect the intended wallet, understand the market, check the order type, review fees, and start with a small test transaction. Confirm whether the action creates token ownership or a derivatives position. After execution, inspect balances, open orders, fills, and transaction history so you know exactly what changed.

Search on Youtube!

Privacy Policy

Terms of Service

Refund Policy

Dydx swap

Dydx swap

Dydx swap fees

ECGSYN - A realistic ECG waveform generator 1.0.0

File: <base> / Java / ecgApplet.java (109,386 bytes)
/************************************************************************/
/*                                                                      */
/* ecgApplet.java                                                       */
/*                                                                      */
/* Copyright (C) 2003 Mauricio Villarroel                               */
/*  (mauricio DOT vllarroel 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/                             */
/*                                                                      */
/************************************************************************/

import javax.swing.*;
import javax.swing.table.*;
import java.awt.*;
import java.text.*;
import java.util.Vector;
import java.io.File;
import java.io.IOException;
import java.awt.event.*;
import java.lang.Math.*;
import java.util.Timer;
import java.util.TimerTask;

/*
 * This class formats a number in decimal notation or in 
 * scientific notation.
 */
class FormatNumber{
    final static DecimalFormat dec1 = new DecimalFormat("0.0");
    final static DecimalFormat dec2 = new DecimalFormat("0.00");

    final static DecimalFormat sci1 = new DecimalFormat("0.0E0");
    final static DecimalFormat sci2 = new DecimalFormat("0.00E0");

    /*
     * Formats the 'number' parameter and returns it as a String.
     * precision = number of decimal places in the output.
     */
    public static String toString( double number,
                                   double upLimit,
                                   double loLimit,
                                   int precision)
    {
        // If number less than decimalLimit, or equal to zero, use decimal style
        if( number == 0.0 ||
            (Math.abs(number) <= upLimit && Math.abs(number) > loLimit) )
        {
            switch (precision){
                case 1 : return dec1.format(number);
                case 2 : return dec2.format(number);
                default: return dec1.format(number);
            }

        } else{
            // Create the format for Scientific Notation with E
            switch (precision){
                case 1 : return sci1.format(number);
                case 2 : return sci2.format(number);
                default: return sci1.format(number);
            }
        }
    }
}

/*
 *
 * Public main class
 */
public class ecgApplet extends javax.swing.JApplet implements AdjustmentListener{

    /**************************************
     * Colors for the Plotting Components *
     **************************************/
    protected Color ecgPlotColor            = Color.BLUE;
    protected Color frameLineColor          = Color.BLACK;
    protected Color frameInsideLineColor    = Color.LIGHT_GRAY;
    protected Color frameFillColor          = Color.WHITE;
    protected Color axesNumColor            = Color.GRAY;
    protected Color titleColor              = Color.BLACK;
    protected Color bgColor                 = Color.WHITE;

    /*********************************************
     * These constants used in drawText() method
     * for placement of the text within a given
     * rectangular area.
     *********************************************/ 
    final int CENTER = 0;
    final int LEFT   = 1;
    final int RIGHT  = 2;

    /*******************
     * Frame Dimensions.
     *******************/
    final int posFrameX =0;
    final int posFrameY =1;
    final int frameHeight =290;
    final int frameAmplitude = frameHeight/2;
    //Coordinates Origin
    final int posOriginY = posFrameY + (frameHeight/2);
    //X coordinates
    final int horzScaleY = posFrameY + frameHeight;
    final int horzScaleWidth = 100;
    final int horzScaleHeight = 20;
    final int fScaleNumSize = 9;
    
    /****************************************************
     * Limit below which scale values use decimal format,
     * above which they use scientific format.
     ****************************************************/
    double upLimit = 100.0;
    double loLimit = 0.01;
    
    /******************************
     * Ploting variables
     ******************************/
    boolean readyToPlot;
    int     plotScrollBarValue;
    double  plotZoom = 0.008;
    double  plotZoomInc = 2;
    /* Flag Variable, show if data has been generated. */
    private boolean ecgGenerated = false;    

    /****************************************************************
     *  GLOBAL ECG PARAMETERS:                                                  
     ****************************************************************/
    private int    N;               /*  Number of heart beats              */
    private double hrstd;           /*  Heart rate std                     */
    private double hrmean;          /*  Heart rate mean                    */
    private double lfhfratio;       /*  LF/HF ratio                        */
    private int    sfecg;           /*  ECG sampling frequency             */        
    private int    sf;              /*  Internal sampling frequency        */
    private double amplitude;       /*  Amplitude for the plot area        */
    private int seed;               /*  Seed                               */    
    private double Anoise;          /*  Amplitude of additive uniform noise*/
    private int    period;
    /* Define frequency parameters for rr process 
     * flo and fhi correspond to the Mayer waves and respiratory rate respectively
     */
    private double flo;             /*  Low frequency                      */
    private double fhi;             /*  High frequency                     */
    private double flostd;          /*  Low frequency std                  */
    private double fhistd;          /*  High frequency std                 */
    /* Order of extrema: [P Q R S T]  */
    private double[] theta = new double[6]; /* ti not in radians*/
    private double[] a = new double[6];
    private double[] b = new double[6];
    
    /*******************************
     * Variable for the Data table
     *******************************/
    private String[] peakStr = {"", "P", "Q", "R", "S", "T"};    

    /******************************************
     * Variables to Animate ECG
     ******************************************/
    //Animating in process?
    private boolean ecgAnimateFlg =false;
    Timer ecgAnimateTimer;
    private long ecgAnimateInterval;
    /* Total plotting Data Table Row */
    private int ecgAnimateNumRows;
    /* Current plotting Data Table Row */
    private int ecgAnimateCurRow;
    /* Plot Area Panel width */
    private int ecgAnimatePanelWidth;
    /* Starting X axis value to plot*/
    private int ecgAnimateInitialZero;
    /* For plotting */
    Point ecgAnimateLastPoint = new java.awt.Point(0, 0);

    /** Initializes the applet ecgApplet */
    public void init() {
        initComponents();
        initWindow();
    }

    private void initWindow(){
        /*********************
        *Init the main Window
        *Set maximize
        *********************/
        try{
            ecgWindow.setMaximum(true);        
        } catch(java.beans.PropertyVetoException e){
            txtStatus.append("Exception Error : " + e + "\n");
        }        

        /*********************
        *Init the data Table
        *********************/
        tableValuesModel = new DefaultTableModel(   new Object [][] {},
                                                    new String [] {"Time", "Voltage", "Peak"}){
                                                        Class[] types = new Class [] {
                                                        java.lang.String.class, java.lang.String.class, java.lang.String.class
                                                    };
                                                    public Class getColumnClass(int columnIndex) {
                                                        return types [columnIndex];
                                                    }
                                                };
        tableValues.setModel(tableValuesModel);

        /* Init the ecgFrame */
        ecgFrame = new ecgPanel();
        ecgFrame.setBackground(new java.awt.Color(255, 255, 255));
        ecgPlotArea.setViewportView(ecgFrame);

        /* Set the ScrollBar */
        plotScrollBar.addAdjustmentListener(this);

        /* Set the size of the Dialogs */
        paramDialog.setBounds(80, 80, 570,500);
        alert.setBounds(80, 80, 540,200);
        helpDialog.setBounds(80, 80, 600,500);

        /*************************
         * Reset all Application 
         * to a init state.
         *************************/        
        resetECG();
    }
    
    private void initComponents() {//GEN-BEGIN:initComponents
        paramDialog = new javax.swing.JDialog();
        paramDesktopPane = new javax.swing.JDesktopPane();
        closeParamDialogButton = new javax.swing.JButton();
        resetParamDialogButton = new javax.swing.JButton();
        saveParamDialogButton = new javax.swing.JButton();
        paramTabbedPane = new javax.swing.JTabbedPane();
        generalInterfacePanel = new javax.swing.JPanel();
        txtSf = new javax.swing.JTextField();
        lblSf = new javax.swing.JLabel();
        lblN = new javax.swing.JLabel();
        txtN = new javax.swing.JTextField();
        lblHrmean = new javax.swing.JLabel();
        txtHrmean = new javax.swing.JTextField();
        lblHrstd = new javax.swing.JLabel();
        txtHrstd = new javax.swing.JTextField();
        lblAmplitude = new javax.swing.JLabel();
        txtAmplitude = new javax.swing.JTextField();
        lblGeneralTitle = new javax.swing.JLabel();
        lblAnoise = new javax.swing.JLabel();
        txtAnoise = new javax.swing.JTextField();
        lblSfecg = new javax.swing.JLabel();
        txtSfecg = new javax.swing.JTextField();
        lblSeed = new javax.swing.JLabel();
        txtSeed = new javax.swing.JTextField();
        spectralCharacteristicsPanel = new javax.swing.JPanel();
        lblSpectralTitle = new javax.swing.JLabel();
        lblLfhfratio = new javax.swing.JLabel();
        txtLfhfratio = new javax.swing.JTextField();
        lblFlo = new javax.swing.JLabel();
        txtFlo = new javax.swing.JTextField();
        lblFhi = new javax.swing.JLabel();
        txtFhi = new javax.swing.JTextField();
        lblFlostd = new javax.swing.JLabel();
        lblFhistd = new javax.swing.JLabel();
        txtFhistd = new javax.swing.JTextField();
        txtFlostd = new javax.swing.JTextField();
        extremaPanel = new javax.swing.JPanel();
        lblMorphologyTitle = new javax.swing.JLabel();
        tiScrollPane = new javax.swing.JScrollPane();
        tiTable = new javax.swing.JTable();
        aiScrollPane = new javax.swing.JScrollPane();
        aiTable = new javax.swing.JTable();
        biScrollPane = new javax.swing.JScrollPane();
        biTable = new javax.swing.JTable();
        ExtremaLabelScrollPane = new javax.swing.JScrollPane();
        ExtremaLabelTable = new javax.swing.JTable();
        paramHelpButton = new javax.swing.JButton();
        alert = new javax.swing.JDialog();
        alertDesktopPane = new javax.swing.JDesktopPane();
        alertText = new javax.swing.JTextArea();
        alertButton = new javax.swing.JButton();
        alertTitle = new javax.swing.JLabel();
        exportDialog = new javax.swing.JDialog();
        helpDialog = new javax.swing.JDialog();
        helpInternalFrame = new javax.swing.JInternalFrame();
        helpScrollPane = new javax.swing.JScrollPane();
        helpEditorPane = new javax.swing.JEditorPane();
        ecgWindow = new javax.swing.JInternalFrame();
        desktopPane = new javax.swing.JDesktopPane();
        jLabel1 = new javax.swing.JLabel();
        jLabel2 = new javax.swing.JLabel();
        jLabel3 = new javax.swing.JLabel();
        jLabel4 = new javax.swing.JLabel();
        jLabel5 = new javax.swing.JLabel();
        jLabel6 = new javax.swing.JLabel();
        jLabel7 = new javax.swing.JLabel();
        lblMaxAmplitude = new javax.swing.JLabel();
        lblOrigin = new javax.swing.JLabel();
        lblMinAmplitude = new javax.swing.JLabel();
        lblXAxis = new javax.swing.JLabel();
        TableScrollPane = new javax.swing.JScrollPane();
        tableValues = new javax.swing.JTable();
        exportButton = new javax.swing.JButton();
        plotScrollBar = new javax.swing.JScrollBar();
        statusScrollPane = new javax.swing.JScrollPane();
        txtStatus = new javax.swing.JTextArea();
        ecgPlotArea = new javax.swing.JScrollPane();
        animateDesktopPane = new javax.swing.JDesktopPane();
        stopAnimateButton = new javax.swing.JButton();
        startAnimateButton = new javax.swing.JButton();
        calculateDesktopPane = new javax.swing.JDesktopPane();
        generateButton = new javax.swing.JButton();
        clearButton = new javax.swing.JButton();
        paramButton = new javax.swing.JButton();
        zommDesktopPane = new javax.swing.JDesktopPane();
        zoomInButton = new javax.swing.JButton();
        zoomOutButton = new javax.swing.JButton();

        paramDialog.setTitle("Set ECG Parameters...");
        paramDialog.setName("paramDialog");
        closeParamDialogButton.setText("Close");
        closeParamDialogButton.addActionListener(new java.awt.event.ActionListener() {
            public void actionPerformed(java.awt.event.ActionEvent evt) {
                closeParamDialogButtonActionPerformed(evt);
            }
        });

        closeParamDialogButton.setBounds(416, 400, 140, 30);
        paramDesktopPane.add(closeParamDialogButton, javax.swing.JLayeredPane.DEFAULT_LAYER);

        resetParamDialogButton.setText("Reset Values");
        resetParamDialogButton.addActionListener(new java.awt.event.ActionListener() {
            public void actionPerformed(java.awt.event.ActionEvent evt) {
                resetParamDialogButtonActionPerformed(evt);
            }
        });

        resetParamDialogButton.setBounds(160, 400, 140, 30);
        paramDesktopPane.add(resetParamDialogButton, javax.swing.JLayeredPane.DEFAULT_LAYER);

        saveParamDialogButton.setText("Save Values");
        saveParamDialogButton.addActionListener(new java.awt.event.ActionListener() {
            public void actionPerformed(java.awt.event.ActionEvent evt) {
                saveParamDialogButtonActionPerformed(evt);
            }
        });

        saveParamDialogButton.setBounds(7, 400, 140, 30);
        paramDesktopPane.add(saveParamDialogButton, javax.swing.JLayeredPane.DEFAULT_LAYER);

        paramTabbedPane.setBorder(new javax.swing.border.BevelBorder(javax.swing.border.BevelBorder.RAISED));
        paramTabbedPane.setTabLayoutPolicy(javax.swing.JTabbedPane.SCROLL_TAB_LAYOUT);
        generalInterfacePanel.setLayout(null);

        generalInterfacePanel.setName("generalInterface");
        txtSf.setToolTipText("");
        txtSf.setInputVerifier(new sfVerifier());
        generalInterfacePanel.add(txtSf);
        txtSf.setBounds(350, 110, 110, 20);

        lblSf.setHorizontalAlignment(javax.swing.SwingConstants.RIGHT);
        lblSf.setText("Internal Sampling frequency [Hz]");
        generalInterfacePanel.add(lblSf);
        lblSf.setBounds(10, 110, 320, 16);

        lblN.setHorizontalAlignment(javax.swing.SwingConstants.RIGHT);
        lblN.setText("Approximate number of heart beats");
        generalInterfacePanel.add(lblN);
        lblN.setBounds(10, 50, 320, 16);

        txtN.setToolTipText("");
        txtN.setInputVerifier(new integerVerifier());
        generalInterfacePanel.add(txtN);
        txtN.setBounds(350, 50, 110, 20);

        lblHrmean.setHorizontalAlignment(javax.swing.SwingConstants.RIGHT);
        lblHrmean.setText("Heart rate mean [bpm]");
        generalInterfacePanel.add(lblHrmean);
        lblHrmean.setBounds(10, 170, 320, 16);

        txtHrmean.setToolTipText("");
        txtHrmean.setInputVerifier(new doubleVerifier());
        generalInterfacePanel.add(txtHrmean);
        txtHrmean.setBounds(350, 170, 110, 20);

        lblHrstd.setHorizontalAlignment(javax.swing.SwingConstants.RIGHT);
        lblHrstd.setText("Heart rate standard deviation [bpm]");
        generalInterfacePanel.add(lblHrstd);
        lblHrstd.setBounds(10, 200, 320, 16);

        txtHrstd.setToolTipText("");
        txtHrstd.setInputVerifier(new doubleVerifier());
        generalInterfacePanel.add(txtHrstd);
        txtHrstd.setBounds(350, 200, 110, 20);

        lblAmplitude.setHorizontalAlignment(javax.swing.SwingConstants.RIGHT);
        lblAmplitude.setText("Plot area Amplitude");
        generalInterfacePanel.add(lblAmplitude);
        lblAmplitude.setBounds(10, 260, 320, 16);

        txtAmplitude.setToolTipText("");
        txtAmplitude.setInputVerifier(new doubleVerifier());
        generalInterfacePanel.add(txtAmplitude);
        txtAmplitude.setBounds(350, 260, 110, 20);

        lblGeneralTitle.setHorizontalAlignment(javax.swing.SwingConstants.CENTER);
        lblGeneralTitle.setText("General Interface Parameters...");
        generalInterfacePanel.add(lblGeneralTitle);
        lblGeneralTitle.setBounds(120, 10, 350, 20);

        lblAnoise.setHorizontalAlignment(javax.swing.SwingConstants.RIGHT);
        lblAnoise.setText("Amplitude of additive uniform noise [mV]");
        lblAnoise.setToolTipText("");
        generalInterfacePanel.add(lblAnoise);
        lblAnoise.setBounds(10, 140, 320, 16);

        txtAnoise.setToolTipText("");
        txtAnoise.setInputVerifier(new doubleVerifier());
        txtAnoise.setName("Anoise");
        generalInterfacePanel.add(txtAnoise);
        txtAnoise.setBounds(350, 140, 110, 20);

        lblSfecg.setHorizontalAlignment(javax.swing.SwingConstants.RIGHT);
        lblSfecg.setText("ECG Sampling Frequency [Hz]");
        generalInterfacePanel.add(lblSfecg);
        lblSfecg.setBounds(10, 80, 320, 16);

        txtSfecg.setToolTipText("");
        txtSfecg.setInputVerifier(new sfecgVerifier());
        generalInterfacePanel.add(txtSfecg);
        txtSfecg.setBounds(350, 80, 110, 20);

        lblSeed.setHorizontalAlignment(javax.swing.SwingConstants.RIGHT);
        lblSeed.setText("Seed");
        generalInterfacePanel.add(lblSeed);
        lblSeed.setBounds(10, 230, 320, 16);

        txtSeed.setToolTipText("");
        txtSeed.setInputVerifier(new integerVerifier());
        generalInterfacePanel.add(txtSeed);
        txtSeed.setBounds(350, 230, 110, 20);

        paramTabbedPane.addTab("General Interface", generalInterfacePanel);

        spectralCharacteristicsPanel.setLayout(null);

        spectralCharacteristicsPanel.setName("spectralCharacteristics");
        lblSpectralTitle.setHorizontalAlignment(javax.swing.SwingConstants.CENTER);
        lblSpectralTitle.setText("Spectral Characteristics Parameters...");
        spectralCharacteristicsPanel.add(lblSpectralTitle);
        lblSpectralTitle.setBounds(120, 10, 350, 20);

        lblLfhfratio.setHorizontalAlignment(javax.swing.SwingConstants.RIGHT);
        lblLfhfratio.setText("LF/HF ratio");
        spectralCharacteristicsPanel.add(lblLfhfratio);
        lblLfhfratio.setBounds(10, 170, 320, 16);

        txtLfhfratio.setToolTipText("Low Frequency / High Frequency ratio");
        txtLfhfratio.setInputVerifier(new doubleVerifier());
        spectralCharacteristicsPanel.add(txtLfhfratio);
        txtLfhfratio.setBounds(350, 170, 110, 20);

        lblFlo.setHorizontalAlignment(javax.swing.SwingConstants.RIGHT);
        lblFlo.setText("Low frequency [Hz]");
        spectralCharacteristicsPanel.add(lblFlo);
        lblFlo.setBounds(10, 50, 320, 16);

        txtFlo.setToolTipText("");
        txtFlo.setInputVerifier(new doubleVerifier());
        spectralCharacteristicsPanel.add(txtFlo);
        txtFlo.setBounds(350, 50, 110, 20);

        lblFhi.setHorizontalAlignment(javax.swing.SwingConstants.RIGHT);
        lblFhi.setText("High frequency [Hz]");
        spectralCharacteristicsPanel.add(lblFhi);
        lblFhi.setBounds(10, 80, 320, 16);

        txtFhi.setToolTipText("");
        txtFhi.setInputVerifier(new doubleVerifier());
        spectralCharacteristicsPanel.add(txtFhi);
        txtFhi.setBounds(350, 80, 110, 20);

        lblFlostd.setHorizontalAlignment(javax.swing.SwingConstants.RIGHT);
        lblFlostd.setText("Low frequency standard deviation [Hz]");
        spectralCharacteristicsPanel.add(lblFlostd);
        lblFlostd.setBounds(10, 110, 320, 16);

        lblFhistd.setHorizontalAlignment(javax.swing.SwingConstants.RIGHT);
        lblFhistd.setText("High frequency standard deviation [Hz]");
        lblFhistd.setToolTipText("");
        spectralCharacteristicsPanel.add(lblFhistd);
        lblFhistd.setBounds(10, 140, 320, 16);

        txtFhistd.setToolTipText("");
        txtFhistd.setInputVerifier(new doubleVerifier());
        txtFhistd.setName("Anoise");
        spectralCharacteristicsPanel.add(txtFhistd);
        txtFhistd.setBounds(350, 140, 110, 20);

        txtFlostd.setToolTipText("");
        txtFlostd.setInputVerifier(new doubleVerifier());
        spectralCharacteristicsPanel.add(txtFlostd);
        txtFlostd.setBounds(350, 110, 110, 20);

        paramTabbedPane.addTab("Spectral Characteristics", spectralCharacteristicsPanel);

        extremaPanel.setLayout(null);

        lblMorphologyTitle.setHorizontalAlignment(javax.swing.SwingConstants.CENTER);
        lblMorphologyTitle.setText("Order of Extrema...");
        extremaPanel.add(lblMorphologyTitle);
        lblMorphologyTitle.setBounds(90, 10, 350, 20);

        tiScrollPane.setBorder(new javax.swing.border.EmptyBorder(new java.awt.Insets(1, 1, 1, 1)));
        tiScrollPane.setViewportBorder(new javax.swing.border.EmptyBorder(new java.awt.Insets(1, 1, 1, 1)));
        tiTable.setModel(new javax.swing.table.DefaultTableModel(
            new Object [][] {
                {null},
                {null},
                {null},
                {null},
                {null}
            },
            new String [] {
                "Theta"
            }
        ) {
            Class[] types = new Class [] {
                java.lang.Double.class
            };

            public Class getColumnClass(int columnIndex) {
                return types [columnIndex];
            }
        });
        tiScrollPane.setViewportView(tiTable);

        extremaPanel.add(tiScrollPane);
        tiScrollPane.setBounds(170, 80, 80, 120);

        aiScrollPane.setBorder(new javax.swing.border.EmptyBorder(new java.awt.Insets(1, 1, 1, 1)));
        aiScrollPane.setViewportBorder(new javax.swing.border.EmptyBorder(new java.awt.Insets(1, 1, 1, 1)));
        aiTable.setModel(new javax.swing.table.DefaultTableModel(
            new Object [][] {
                {null},
                {null},
                {null},
                {null},
                {null}
            },
            new String [] {
                "a"
            }
        ) {
            Class[] types = new Class [] {
                java.lang.Double.class
            };

            public Class getColumnClass(int columnIndex) {
                return types [columnIndex];
            }
        });
        aiScrollPane.setViewportView(aiTable);

        extremaPanel.add(aiScrollPane);
        aiScrollPane.setBounds(280, 80, 80, 120);

        biScrollPane.setBorder(new javax.swing.border.EmptyBorder(new java.awt.Insets(1, 1, 1, 1)));
        biScrollPane.setViewportBorder(new javax.swing.border.EmptyBorder(new java.awt.Insets(1, 1, 1, 1)));
        biTable.setModel(new javax.swing.table.DefaultTableModel(
            new Object [][] {
                {null},
                {null},
                {null},
                {null},
                {null}
            },
            new String [] {
                "b"
            }
        ) {
            Class[] types = new Class [] {
                java.lang.Double.class
            };

            public Class getColumnClass(int columnIndex) {
                return types [columnIndex];
            }
        });
        biScrollPane.setViewportView(biTable);

        extremaPanel.add(biScrollPane);
        biScrollPane.setBounds(390, 80, 80, 120);

        ExtremaLabelScrollPane.setBorder(new javax.swing.border.EmptyBorder(new java.awt.Insets(1, 1, 1, 1)));
        ExtremaLabelScrollPane.setHorizontalScrollBarPolicy(javax.swing.JScrollPane.HORIZONTAL_SCROLLBAR_NEVER);
        ExtremaLabelScrollPane.setVerticalScrollBarPolicy(javax.swing.JScrollPane.VERTICAL_SCROLLBAR_NEVER);
        ExtremaLabelScrollPane.setViewportBorder(new javax.swing.border.EmptyBorder(new java.awt.Insets(1, 1, 1, 1)));
        ExtremaLabelScrollPane.setEnabled(false);
        ExtremaLabelTable.setBackground((java.awt.Color) javax.swing.UIManager.getDefaults().get("Button.background"));
        ExtremaLabelTable.setFont(new java.awt.Font("Dialog", 1, 12));
        ExtremaLabelTable.setModel(new javax.swing.table.DefaultTableModel(
            new Object [][] {
                {"          P(1) :"},
                {"          Q(2) :"},
                {"          R(3) :"},
                {"          S(4) :"},
                {"          T(5) :"}
            },
            new String [] {
                "peak label"
            }
        ) {
            Class[] types = new Class [] {
                java.lang.String.class
            };
            boolean[] canEdit = new boolean [] {
                false
            };

            public Class getColumnClass(int columnIndex) {
                return types [columnIndex];
            }

            public boolean isCellEditable(int rowIndex, int columnIndex) {
                return canEdit [columnIndex];
            }
        });
        ExtremaLabelTable.setGridColor((java.awt.Color) javax.swing.UIManager.getDefaults().get("Button.background"));
        ExtremaLabelTable.setEnabled(false);
        ExtremaLabelScrollPane.setViewportView(ExtremaLabelTable);

        extremaPanel.add(ExtremaLabelScrollPane);
        ExtremaLabelScrollPane.setBounds(40, 80, 110, 120);

        paramTabbedPane.addTab("ECG Morphology", extremaPanel);

        paramTabbedPane.setBounds(7, 10, 550, 380);
        paramDesktopPane.add(paramTabbedPane, javax.swing.JLayeredPane.DEFAULT_LAYER);

        paramHelpButton.setText("Help");
        paramHelpButton.setEnabled(false);
        paramHelpButton.addActionListener(new java.awt.event.ActionListener() {
            public void actionPerformed(java.awt.event.ActionEvent evt) {
                paramHelpButtonActionPerformed(evt);
            }
        });

        paramHelpButton.setBounds(310, 400, 100, 30);
        paramDesktopPane.add(paramHelpButton, javax.swing.JLayeredPane.DEFAULT_LAYER);

        paramDialog.getContentPane().add(paramDesktopPane, java.awt.BorderLayout.CENTER);

        alert.setModal(true);
        alertText.setBackground((java.awt.Color) javax.swing.UIManager.getDefaults().get("Desktop.background"));
        alertText.setEditable(false);
        alertText.setLineWrap(true);
        alertText.setRows(10);
        alertText.setWrapStyleWord(true);
        alertText.setBounds(10, 10, 520, 80);
        alertDesktopPane.add(alertText, javax.swing.JLayeredPane.DEFAULT_LAYER);

        alertButton.setText("OK");
        alertButton.addActionListener(new java.awt.event.ActionListener() {
            public void actionPerformed(java.awt.event.ActionEvent evt) {
                alertButtonActionPerformed(evt);
            }
        });

        alertButton.setBounds(230, 110, 80, -1);
        alertDesktopPane.add(alertButton, javax.swing.JLayeredPane.DEFAULT_LAYER);

        alertTitle.setBounds(0, 0, 350, -1);
        alertDesktopPane.add(alertTitle, javax.swing.JLayeredPane.DEFAULT_LAYER);

        alert.getContentPane().add(alertDesktopPane, java.awt.BorderLayout.CENTER);

        exportDialog.setModal(true);
        helpInternalFrame.setTitle("ECG Help");
        helpInternalFrame.setVisible(true);
        helpEditorPane.setEditable(false);
        helpScrollPane.setViewportView(helpEditorPane);

        helpInternalFrame.getContentPane().add(helpScrollPane, java.awt.BorderLayout.CENTER);

        helpDialog.getContentPane().add(helpInternalFrame, java.awt.BorderLayout.CENTER);

        setFocusCycleRoot(false);
        setName("ecgpanel");
        ecgWindow.setBorder(new javax.swing.border.BevelBorder(javax.swing.border.BevelBorder.RAISED));
        ecgWindow.setTitle("Electrocardiogram Signals (ECG)");
        try {
            ecgWindow.setSelected(true);
        } catch (java.beans.PropertyVetoException e1) {
            e1.printStackTrace();
        }
        ecgWindow.setVisible(true);
        jLabel1.setHorizontalAlignment(javax.swing.SwingConstants.CENTER);
        jLabel1.setText("V");
        jLabel1.setBounds(0, 110, 20, 16);
        desktopPane.add(jLabel1, javax.swing.JLayeredPane.DEFAULT_LAYER);

        jLabel2.setHorizontalAlignment(javax.swing.SwingConstants.CENTER);
        jLabel2.setText("o");
        jLabel2.setBounds(0, 130, 20, 16);
        desktopPane.add(jLabel2, javax.swing.JLayeredPane.DEFAULT_LAYER);

        jLabel3.setHorizontalAlignment(javax.swing.SwingConstants.CENTER);
        jLabel3.setText("l");
        jLabel3.setBounds(0, 150, 20, 16);
        desktopPane.add(jLabel3, javax.swing.JLayeredPane.DEFAULT_LAYER);

        jLabel4.setHorizontalAlignment(javax.swing.SwingConstants.CENTER);
        jLabel4.setText("t");
        jLabel4.setBounds(0, 170, 20, 16);
        desktopPane.add(jLabel4, javax.swing.JLayeredPane.DEFAULT_LAYER);

        jLabel5.setHorizontalAlignment(javax.swing.SwingConstants.CENTER);
        jLabel5.setText("a");
        jLabel5.setBounds(0, 190, 20, 16);
        desktopPane.add(jLabel5, javax.swing.JLayeredPane.DEFAULT_LAYER);

        jLabel6.setHorizontalAlignment(javax.swing.SwingConstants.CENTER);
        jLabel6.setText("g");
        jLabel6.setBounds(0, 210, 20, 16);
        desktopPane.add(jLabel6, javax.swing.JLayeredPane.DEFAULT_LAYER);

        jLabel7.setHorizontalAlignment(javax.swing.SwingConstants.CENTER);
        jLabel7.setText("e");
        jLabel7.setBounds(0, 230, 20, 16);
        desktopPane.add(jLabel7, javax.swing.JLayeredPane.DEFAULT_LAYER);

        lblMaxAmplitude.setFont(new java.awt.Font("Dialog", 1, 9));
        lblMaxAmplitude.setText("0.001");
        lblMaxAmplitude.setBounds(30, 25, 40, 12);
        desktopPane.add(lblMaxAmplitude, javax.swing.JLayeredPane.DEFAULT_LAYER);

        lblOrigin.setFont(new java.awt.Font("Dialog", 1, 9));
        lblOrigin.setText("0.00");
        lblOrigin.setBounds(30, 167, 40, 12);
        desktopPane.add(lblOrigin, javax.swing.JLayeredPane.DEFAULT_LAYER);

        lblMinAmplitude.setFont(new java.awt.Font("Dialog", 1, 9));
        lblMinAmplitude.setText("-0.001");
        lblMinAmplitude.setBounds(30, 309, 40, 12);
        desktopPane.add(lblMinAmplitude, javax.swing.JLayeredPane.DEFAULT_LAYER);

        lblXAxis.setHorizontalAlignment(javax.swing.SwingConstants.CENTER);
        lblXAxis.setText("Time");
        lblXAxis.setBounds(73, 367, 580, 16);
        desktopPane.add(lblXAxis, javax.swing.JLayeredPane.DEFAULT_LAYER);

        TableScrollPane.setBorder(new javax.swing.border.TitledBorder(null, "Data Table", javax.swing.border.TitledBorder.CENTER, javax.swing.border.TitledBorder.ABOVE_TOP));
        TableScrollPane.setHorizontalScrollBarPolicy(javax.swing.JScrollPane.HORIZONTAL_SCROLLBAR_ALWAYS);
        TableScrollPane.setVerticalScrollBarPolicy(javax.swing.JScrollPane.VERTICAL_SCROLLBAR_ALWAYS);
        tableValues.setAutoResizeMode(javax.swing.JTable.AUTO_RESIZE_OFF);
        TableScrollPane.setViewportView(tableValues);

        TableScrollPane.setBounds(658, 2, 250, 510);
        desktopPane.add(TableScrollPane, javax.swing.JLayeredPane.DEFAULT_LAYER);

        exportButton.setText("Export Table Data...");
        exportButton.setEnabled(false);
        exportButton.addActionListener(new java.awt.event.ActionListener() {
            public void actionPerformed(java.awt.event.ActionEvent evt) {
                exportButtonActionPerformed(evt);
            }
        });

        exportButton.setBounds(658, 515, 250, 20);
        desktopPane.add(exportButton, javax.swing.JLayeredPane.DEFAULT_LAYER);

        plotScrollBar.setMaximum(0);
        plotScrollBar.setOrientation(javax.swing.JScrollBar.HORIZONTAL);
        plotScrollBar.setName("timeScroll");
        plotScrollBar.setBounds(73, 352, 580, 17);
        desktopPane.add(plotScrollBar, javax.swing.JLayeredPane.DEFAULT_LAYER);

        statusScrollPane.setBorder(new javax.swing.border.TitledBorder(new javax.swing.border.BevelBorder(javax.swing.border.BevelBorder.RAISED), "Status and Messages:", javax.swing.border.TitledBorder.LEFT, javax.swing.border.TitledBorder.ABOVE_TOP));
        statusScrollPane.setVerticalScrollBarPolicy(javax.swing.JScrollPane.VERTICAL_SCROLLBAR_ALWAYS);
        txtStatus.setEditable(false);
        statusScrollPane.setViewportView(txtStatus);

        statusScrollPane.setBounds(234, 385, 420, 150);
        desktopPane.add(statusScrollPane, javax.swing.JLayeredPane.DEFAULT_LAYER);

        ecgPlotArea.setBorder(new javax.swing.border.TitledBorder(new javax.swing.border.BevelBorder(javax.swing.border.BevelBorder.RAISED), "Plot Area", javax.swing.border.TitledBorder.CENTER, javax.swing.border.TitledBorder.ABOVE_TOP));
        ecgPlotArea.setHorizontalScrollBarPolicy(javax.swing.JScrollPane.HORIZONTAL_SCROLLBAR_NEVER);
        ecgPlotArea.setVerticalScrollBarPolicy(javax.swing.JScrollPane.VERTICAL_SCROLLBAR_NEVER);
        ecgPlotArea.setBounds(73, 2, 580, 350);
        desktopPane.add(ecgPlotArea, javax.swing.JLayeredPane.DEFAULT_LAYER);

        animateDesktopPane.setBorder(new javax.swing.border.TitledBorder(new javax.swing.border.LineBorder(new java.awt.Color(0, 0, 0)), "Animate"));
        stopAnimateButton.setFont(new java.awt.Font("Dialog", 1, 11));
        stopAnimateButton.setText("Stop");
        stopAnimateButton.setEnabled(false);
        stopAnimateButton.addActionListener(new java.awt.event.ActionListener() {
            public void actionPerformed(java.awt.event.ActionEvent evt) {
                stopAnimateButtonActionPerformed(evt);
            }
        });

        stopAnimateButton.setBounds(120, 17, 90, 20);
        animateDesktopPane.add(stopAnimateButton, javax.swing.JLayeredPane.DEFAULT_LAYER);

        startAnimateButton.setFont(new java.awt.Font("Dialog", 1, 11));
        startAnimateButton.setText("Start");
        startAnimateButton.setEnabled(false);
        startAnimateButton.addActionListener(new java.awt.event.ActionListener() {
            public void actionPerformed(java.awt.event.ActionEvent evt) {
                startAnimateButtonActionPerformed(evt);
            }
        });

        startAnimateButton.setBounds(7, 17, 90, 20);
        animateDesktopPane.add(startAnimateButton, javax.swing.JLayeredPane.DEFAULT_LAYER);

        animateDesktopPane.setBounds(7, 492, 220, 45);
        desktopPane.add(animateDesktopPane, javax.swing.JLayeredPane.DEFAULT_LAYER);

        calculateDesktopPane.setBorder(new javax.swing.border.TitledBorder(new javax.swing.border.LineBorder(new java.awt.Color(0, 0, 0)), "Calculate ECG"));
        generateButton.setFont(new java.awt.Font("Dialog", 1, 11));
        generateButton.setText("Generate");
        generateButton.addActionListener(new java.awt.event.ActionListener() {
            public void actionPerformed(java.awt.event.ActionEvent evt) {
                generateButtonActionPerformed(evt);
            }
        });

        generateButton.setBounds(7, 17, 90, 20);
        calculateDesktopPane.add(generateButton, javax.swing.JLayeredPane.DEFAULT_LAYER);

        clearButton.setFont(new java.awt.Font("Dialog", 1, 11));
        clearButton.setText("Clear");
        clearButton.setEnabled(false);
        clearButton.addActionListener(new java.awt.event.ActionListener() {
            public void actionPerformed(java.awt.event.ActionEvent evt) {
                clearButtonActionPerformed(evt);
            }
        });

        clearButton.setBounds(120, 17, 90, 20);
        calculateDesktopPane.add(clearButton, javax.swing.JLayeredPane.DEFAULT_LAYER);

        paramButton.setFont(new java.awt.Font("Dialog", 1, 11));
        paramButton.setText("Set Parameters...");
        paramButton.addActionListener(new java.awt.event.ActionListener() {
            public void actionPerformed(java.awt.event.ActionEvent evt) {
                paramButtonActionPerformed(evt);
            }
        });

        paramButton.setBounds(7, 42, 130, 20);
        calculateDesktopPane.add(paramButton, javax.swing.JLayeredPane.DEFAULT_LAYER);

        calculateDesktopPane.setBounds(7, 377, 220, 70);
        desktopPane.add(calculateDesktopPane, javax.swing.JLayeredPane.DEFAULT_LAYER);

        zommDesktopPane.setBorder(new javax.swing.border.TitledBorder(new javax.swing.border.LineBorder(new java.awt.Color(0, 0, 0)), "Zoom plot area"));
        zoomInButton.setFont(new java.awt.Font("Dialog", 1, 14));
        zoomInButton.setText("+");
        zoomInButton.setEnabled(false);
        zoomInButton.addActionListener(new java.awt.event.ActionListener() {
            public void actionPerformed(java.awt.event.ActionEvent evt) {
                zoomInButtonActionPerformed(evt);
            }
        });

        zoomInButton.setBounds(120, 17, 90, 20);
        zommDesktopPane.add(zoomInButton, javax.swing.JLayeredPane.DEFAULT_LAYER);

        zoomOutButton.setFont(new java.awt.Font("Dialog", 1, 14));
        zoomOutButton.setText("-");
        zoomOutButton.setEnabled(false);
        zoomOutButton.addActionListener(new java.awt.event.ActionListener() {
            public void actionPerformed(java.awt.event.ActionEvent evt) {
                zoomOutButtonActionPerformed(evt);
            }
        });

        zoomOutButton.setBounds(7, 17, 90, 20);
        zommDesktopPane.add(zoomOutButton, javax.swing.JLayeredPane.DEFAULT_LAYER);

        zommDesktopPane.setBounds(7, 447, 220, 45);
        desktopPane.add(zommDesktopPane, javax.swing.JLayeredPane.DEFAULT_LAYER);

        ecgWindow.getContentPane().add(desktopPane, java.awt.BorderLayout.CENTER);

        getContentPane().add(ecgWindow, java.awt.BorderLayout.CENTER);

    }//GEN-END:initComponents

    private void paramHelpButtonActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_paramHelpButtonActionPerformed
        helpEditorPane.setContentType("text/html");
        helpEditorPane.setText( "<html> <body>" +
                                "<font size='3'>"+
                                "<table width='400' border='0' align='center' cellpadding='0' cellspacing='5'>" +
                                "  <tr> " +
                                "    <td height='50' align='left' valign='middle'><strong><font color='#FF0000' size='4' face='Arial, Helvetica, sans-serif'>ECGSYN " +
                                "      PARAMETER DOCUMENTATION</font></strong></td>" +
                                "  </tr>" +
                                "  <tr> " +
                                "       <td height='30' align='left' valign='top'><strong><font color='#0000FF'>GENERAL " +
                                "           INTERFACE</font></strong></td> "+
                                "  </tr>" +
                                "  <tr> " +
                                "      <td align='left' valign='top'> <table width='100%' border='0' cellspacing='5' cellpadding='5'>" +
                                "    <tr> " +
                                "      <td width='60'>&nbsp;</td>" +
                                "      <td><strong>Approximate number of heart beats:</strong><br>" +
                                "        fjaklsdfjasldjasdjfgfagsdgfasdgfywerwaegsdgfwefasjkgiawegasfkgiawefaklsdgfaiwegfklasegfaiwegaklgawiegfafwgegfauiwegfkasdgfiawegfawiegfasdklgfialweugfasefiuawgefilugfawileugfawilegfaweilgfawilegfasiduflskdgfailwuegfawileusdjklgfawilesdfk</td>" +
                                "    </tr>" +
                                "    <tr>" +
                                "      <td width='60'>&nbsp;</td>" +
                                "      <td><strong><dt>ECG Sampling Frequency</dt></strong>" +
                                "        <dd>definition</dd></td>" +
                                "    </tr>" +
                                "    <tr>" +
                                "      <td width='60'>&nbsp;</td>" +
                                "      <td><strong><dt>Internal Sampling Frequency</dt></strong>" +
                                "        <dd>definition</dd></td>" +
                                "    </tr>" +
                                "    <tr>" +
                                "      <td width='60'>&nbsp;</td>" +
                                "      <td><strong><dt>Amplitude of additive uniform noise</dt></strong>" +
                                "        <dd>definition</dd></td>" +
                                "    </tr>" +
                                "    <tr>" +
                                "      <td width='60'>&nbsp;</td>" +
                                "      <td><strong><dt>Heart rate mean</dt></strong>" +
                                "        <dd>definition</dd></td>" +
                                "    </tr>" +
                                "    <tr>" +
                                "      <td width='60'>&nbsp;</td>" +
                                "      <td><strong><dt>Heart rate standard deviation</dt></strong>" +
                                "        <dd>definition</dd></td>" +
                                "    </tr>" +
                                "    <tr>" +
                                "      <td width='60'>&nbsp;</td>" +
                                "      <td><strong><dt>Seed</dt></strong>" +
                                "        <dd>definition</dd></td>" +
                                "    </tr>" +
                                "  </table></td>" +
                                "  </tr>" +
                                "</table>" +
                                "</font>" +
                                "</body> </html>");
        helpDialog.show();
    }//GEN-LAST:event_paramHelpButtonActionPerformed

    private void alertButtonActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_alertButtonActionPerformed
        alert.hide();
    }//GEN-LAST:event_alertButtonActionPerformed

    private void zoomInButtonActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_zoomInButtonActionPerformed
        desktopPane.setCursor(java.awt.Cursor.getPredefinedCursor(java.awt.Cursor.WAIT_CURSOR));
 
        paramDialog.hide();
        plotZoom = plotZoom / plotZoomInc;
        ecgFrame.repaint();

        desktopPane.setCursor(java.awt.Cursor.getPredefinedCursor(java.awt.Cursor.DEFAULT_CURSOR));        
    }//GEN-LAST:event_zoomInButtonActionPerformed

    private void zoomOutButtonActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_zoomOutButtonActionPerformed
        desktopPane.setCursor(java.awt.Cursor.getPredefinedCursor(java.awt.Cursor.WAIT_CURSOR));

        paramDialog.hide();
        plotZoom = plotZoom * plotZoomInc;
        ecgFrame.repaint();

        desktopPane.setCursor(java.awt.Cursor.getPredefinedCursor(java.awt.Cursor.DEFAULT_CURSOR));
    }//GEN-LAST:event_zoomOutButtonActionPerformed

    private void startAnimateButtonActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_startAnimateButtonActionPerformed
        paramDialog.hide();
        // Disabling automatic plot
        readyToPlot = false;
        ecgFrame.repaint();

        /*
         * Initialize ECG Animate variables
         */
        ecgAnimateFlg = true;
        ecgAnimateNumRows = tableValuesModel.getRowCount();
        ecgAnimateCurRow = 0;
        ecgAnimatePanelWidth = ecgFrame.getBounds().width;
        ecgAnimateInitialZero = 0;
        ecgAnimateLastPoint.setLocation(0, posOriginY - (int)(Double.valueOf(tableValues.getValueAt(0, 1).toString()).doubleValue() * frameAmplitude / amplitude));
        
        /* Create Timer */
        ecgAnimateTimer = new Timer();
        /* Schedule the Animate Plotting Task */
        ecgAnimateTimer.scheduleAtFixedRate(new ECGAnimate(), 0, ecgAnimateInterval);
        
        /* Set the Animate Buttons */
        startECGAnimationSetControls();
    }//GEN-LAST:event_startAnimateButtonActionPerformed

    private void saveParamDialogButtonActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_saveParamDialogButtonActionPerformed
        saveParametersValues();
        paramDialog.hide();        
    }//GEN-LAST:event_saveParamDialogButtonActionPerformed

    private void resetParamDialogButtonActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_resetParamDialogButtonActionPerformed
        clearParameters();
    }//GEN-LAST:event_resetParamDialogButtonActionPerformed

    private void closeParamDialogButtonActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_closeParamDialogButtonActionPerformed
        paramDialog.hide();        
    }//GEN-LAST:event_closeParamDialogButtonActionPerformed

    private void paramButtonActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_paramButtonActionPerformed
        paramDialog.show();
    }//GEN-LAST:event_paramButtonActionPerformed

    private void exportButtonActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_exportButtonActionPerformed
        paramDialog.hide();
        exportDialog.setBounds(80, 80,500,500);
        exportDialog.show();     
    }//GEN-LAST:event_exportButtonActionPerformed

    private void saveParametersValues(){
        txtStatus.setText(null);
        checkParameters();
    }

    private void clearDataTable(){
        // Delete the DataTable
        tableValuesModel.setRowCount(0);
    }

    /**
     *   Draw a string in the center of a given box.
     *   Reduce the font size if necessary to fit. Can
     *   fix the type size to a value passed as an argument.
     *   The position of the string within the box passed
     *   as LEFT, CENTER or RIGHT constant value.
     *   Don't draw the strings if they do not fit.
     */
    private int drawText(   Graphics g, String msg, int xBox, int yBox, int boxWidth, int boxHeight,
                            int fixedTypeSizeValue, int position){
        boolean fixedTypeSize = false;
        int typeSize = 24;

        // Fixed to a particular type size.
        if(fixedTypeSizeValue > 0) {
            fixedTypeSize = true;
            typeSize = fixedTypeSizeValue;
        }

        int typeSizeMin = 8;
        int x=xBox,y=yBox;
        do {
            // Create the font and pass it to the  Graphics context
            g.setFont(new Font("Monospaced",Font.PLAIN,typeSize));

            // Get measures needed to center the message
            FontMetrics fm = g.getFontMetrics();

            // How many pixels wide is the string
            int msgWidth = fm.stringWidth(msg);

            // How tall is the text?
            int msgHeight = fm.getHeight();

            // See if the text will fit in the allotted
            // vertical limits
            if( msgHeight < boxHeight && msgWidth < boxWidth) {
                y = yBox + boxHeight/2 +(msgHeight/2);
                if( position == CENTER)
                    x = xBox + boxWidth/2 - (msgWidth/2);
                else if(position == RIGHT)
                    x = xBox + boxWidth - msgWidth;
                else
                    x = xBox;

                break;
            }

            // If fixedTypeSize and wouldn't fit, don't draw.
            if( fixedTypeSize) return -1;

            // Try smaller type
            typeSize -= 2;

        } while (typeSize >= typeSizeMin);

        // Don't display the numbers if they did not fit
        if( typeSize < typeSizeMin) return -1;

        // Otherwise, draw and return positive signal.
        g.drawString(msg,x,y);
//                ecgFrame.revalidate();
//                ecgFrame.repaint();        
        return typeSize;
    }

    /*
     * ReInit the Button Parameters' values
     */
    private void clearParameters(){
        /* General Intergace parameters */
        txtN.setText("256");
        N = 256;
        
        txtSfecg.setText("256");
        sfecg = 256;        

        txtSf.setText("512");
        sf = 512;        
 
        txtAnoise.setText("0.1");
        Anoise = 0.1;

        txtHrmean.setText("60.0");
        hrmean = 60.0;

        txtHrstd.setText("1.0");
        hrstd = 1.0;

        txtSeed.setText("1");
        seed = 1;

        txtAmplitude.setText("1.4");
        amplitude = 1.4;
        
        /* Spectral Characteristics parameters */
        txtFlo.setText("0.1");
        flo = 0.1;

        txtFhi.setText("0.25");
        fhi = 0.25;

        txtFlostd.setText("0.01");
        flostd = 0.01;

        txtFhistd.setText("0.01");
        fhistd = 0.01;      

        txtLfhfratio.setText("0.5");
        lfhfratio = 0.5;

        /*
         * ECG morphology: Order of extrema: [P Q R S T]
         */
        theta[1]= -60.0;
        theta[2]= -15.0;
        theta[3]= 0.0;
        theta[4]= 15.0;
        theta[5]= 90.0;

        a[1]= 1.2;
        a[2]= -5.0;
        a[3]= 30.0;
        a[4]= -7.5;
        a[5]= 0.75;
        
        b[1]= 0.25;
        b[2]= 0.1;
        b[3]= 0.1;
        b[4]= 0.1;
        b[5]= 0.4;

        //data tables
        tiTable.getModel().setValueAt(new Double(theta[1]), 0, 0);
        tiTable.getModel().setValueAt(new Double(theta[2]), 1, 0);
        tiTable.getModel().setValueAt(new Double(theta[3]), 2, 0);
        tiTable.getModel().setValueAt(new Double(theta[4]), 3, 0);
        tiTable.getModel().setValueAt(new Double(theta[5]), 4, 0);

        aiTable.getModel().setValueAt(new Double(a[1]), 0, 0);
        aiTable.getModel().setValueAt(new Double(a[2]), 1, 0);
        aiTable.getModel().setValueAt(new Double(a[3]), 2, 0);
        aiTable.getModel().setValueAt(new Double(a[4]), 3, 0);
        aiTable.getModel().setValueAt(new Double(a[5]), 4, 0);

        biTable.getModel().setValueAt(new Double(b[1]), 0, 0);
        biTable.getModel().setValueAt(new Double(b[2]), 1, 0);
        biTable.getModel().setValueAt(new Double(b[3]), 2, 0);
        biTable.getModel().setValueAt(new Double(b[4]), 3, 0);
        biTable.getModel().setValueAt(new Double(b[5]), 4, 0);        
        
        /*
         * ECG Animate parameters
         */
        // convert into miliseconds interval
        ecgAnimateInterval = (long)(1000/(sfecg));
    }

    private void resetECG(){
        ecgGenerated = false;
        clearParameters();
        resetPlotArea();
        resetButtons();
        resetStatusBar();
    }

    /*
     * Set the appropiate state of the controls for start the ECG Animation
     */
    private void startECGAnimationSetControls(){
        stopAnimateButton.setEnabled(true);

        //exportButton.setEnabled(true);
        clearButton.setEnabled(false);
        generateButton.setEnabled(false);
        paramButton.setEnabled(false);

        zoomInButton.setEnabled(false);
        zoomOutButton.setEnabled(false);
        
        startAnimateButton.setEnabled(false);
    }

    /*
     * Set the appropiate state of the controls for stop the ECG Animation
     */
    private void stopECGAnimationSetControls(){
        startAnimateButton.setEnabled(true);

        clearButton.setEnabled(true);
        generateButton.setEnabled(true);
        paramButton.setEnabled(true);

        zoomInButton.setEnabled(true);
        zoomOutButton.setEnabled(true);

        stopAnimateButton.setEnabled(false);
    }    

    /*
     * Enable the buttons after generating the ecg Data.
     */
    private void enableButtons(){
        startAnimateButton.setEnabled(true);
        exportButton.setEnabled(true);
        clearButton.setEnabled(true);
        zoomInButton.setEnabled(true);
        zoomOutButton.setEnabled(true);
    }

    private void resetButtons(){
        stopAnimateButton.setEnabled(false);
        startAnimateButton.setEnabled(false);
        exportButton.setEnabled(false);        
        clearButton.setEnabled(false);
        zoomInButton.setEnabled(false);
        zoomOutButton.setEnabled(false);
    }

    private void resetPlotArea(){
        lblMaxAmplitude.setText("1.4");
        lblMinAmplitude.setText("-1.4");
        readyToPlot = false;
        plotScrollBarValue = 0;
    }

    private void resetStatusBar(){
        txtStatus.setText(null);
        txtStatus.append("**********************************************************\n");
        txtStatus.append("Change desired ECG Parameters\n");
        txtStatus.append("and then click 'Generate' button to generate and plot data\n");
        txtStatus.append("**********************************************************");
    }

    private void clearButtonActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_clearButtonActionPerformed
        desktopPane.setCursor(java.awt.Cursor.getPredefinedCursor(java.awt.Cursor.WAIT_CURSOR));

        paramDialog.hide();
        // Delete the DataTable
        clearDataTable();
        resetECG();
        ecgFrame.repaint();

        desktopPane.setCursor(java.awt.Cursor.getPredefinedCursor(java.awt.Cursor.DEFAULT_CURSOR));        
    }//GEN-LAST:event_clearButtonActionPerformed

    private void stopAnimateButtonActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_stopAnimateButtonActionPerformed
        paramDialog.hide();
        /* Stop the Animate Plotting Task */
        ecgAnimateTimer.cancel();
        ecgAnimateFlg =false;
        /* Enable automatic plot */
        readyToPlot = true;
        /* Repaint Plot Area */
        ecgFrame.repaint();

        /* Set the Animate Buttons */
        stopECGAnimationSetControls();
    }//GEN-LAST:event_stopAnimateButtonActionPerformed

    private boolean checkParameters(){
        txtStatus.append("Starting to check ECG parameters entered...\n");

        boolean RetValue = true;
        //ECG Sampling frequency flag
        boolean sfecg_flg = true;
        //Internal Sampling frequency flag
        boolean sf_flg = true;

        /* General Intergace parameters */
        try {
            N = Integer.valueOf(txtN.getText()).intValue();
        } catch(java.lang.NumberFormatException e){
            txtStatus.append("Incorrect 'Approximate number of heart beats' entered, please correct it!\n");
            txtStatus.append("Exception Error : " + e + "\n");
            RetValue = false;
        }

        try {
            sfecg = Integer.valueOf(txtSfecg.getText()).intValue();
        } catch(java.lang.NumberFormatException e){
            txtStatus.append("Incorrect 'ECG Sampling Frequency' entered, please correct it!\n");
            txtStatus.append("Exception Error : " + e + "\n");
            sfecg_flg = false;
            RetValue = false;
        }

        if(sfecg_flg){
            try {
                sf = Integer.valueOf(txtSf.getText()).intValue();
            } catch(java.lang.NumberFormatException e){
                txtStatus.append("Incorrect 'Internal Sampling Frequency' entered, please correct it!\n");
                txtStatus.append("Exception Error : " + e + "\n");
                sf_flg = false;
                RetValue = false;
            }
        }

        // Check the Internal frequency respect to ECG frequency
        if(sfecg_flg && sf_flg){
            if(((int)Math.IEEEremainder(sf, sfecg)) != 0){
                txtStatus.append("Internal sampling frequency must be an integer multiple of the\n"); 
                txtStatus.append("ECG sampling frequency!, that currently is = " + sfecg + " Hertz\n"); 
                RetValue = false;
            }
        }

        try {
            Anoise = Double.valueOf(txtAnoise.getText()).doubleValue();
        } catch(java.lang.NumberFormatException e){
            txtStatus.append("Incorrect 'Amplitude of additive uniform noise' entered, please correct it!\n");
            txtStatus.append("Exception Error : " + e + "\n");
            RetValue = false;
        }

        try {
            hrmean = Double.valueOf(txtHrmean.getText()).doubleValue();
        } catch(java.lang.NumberFormatException e){
            txtStatus.append("Incorrect 'Heart rate mean' entered, please correct it!\n");
            txtStatus.append("Exception Error : " + e + "\n");
            RetValue = false;
        }

        try {
            hrstd = Double.valueOf(txtHrstd.getText()).doubleValue();
        } catch(java.lang.NumberFormatException e){
            txtStatus.append("Incorrect 'Heart rate standard deviation' entered, please correct it!\n");
            txtStatus.append("Exception Error : " + e + "\n");
            RetValue = false;
        }
        
        try {
            seed = Integer.valueOf(txtSeed.getText()).intValue();
        } catch(java.lang.NumberFormatException e){
            txtStatus.append("Incorrect 'seed' entered, please correct it!\n");
            txtStatus.append("Exception Error : " + e + "\n");
            RetValue = false;
        }
       
        try {
            amplitude = Double.valueOf(txtAmplitude.getText()).doubleValue();
        } catch(java.lang.NumberFormatException e){
            txtStatus.append("Incorrect 'Plot Area Amplitude' entered, please correct it!\n");
            txtStatus.append("Exception Error : " + e + "\n");
            RetValue = false;
        }

        /* Spectral Characteristics parameters */

        try {
            flo = Double.valueOf(txtFlo.getText()).doubleValue();
        } catch(java.lang.NumberFormatException e){
            txtStatus.append("Incorrect 'Low frequency' entered, please correct it!\n");
            txtStatus.append("Exception Error : " + e + "\n");
            RetValue = false;
        }

        try {
            fhi = Double.valueOf(txtFhi.getText()).doubleValue();
        } catch(java.lang.NumberFormatException e){
            txtStatus.append("Incorrect 'High frequency' entered, please correct it!\n");
            txtStatus.append("Exception Error : " + e + "\n");
            RetValue = false;
        }

        try {
            flostd = Double.valueOf(txtFlostd.getText()).doubleValue();
        } catch(java.lang.NumberFormatException e){
            txtStatus.append("Incorrect 'Low frequency standard deviation' entered, please correct it!\n");
            txtStatus.append("Exception Error : " + e + "\n");
            RetValue = false;
        }

        try {
            fhistd = Double.valueOf(txtFhistd.getText()).doubleValue();
        } catch(java.lang.NumberFormatException e){
            txtStatus.append("Incorrect 'High frequency standard deviation' entered, please correct it!\n");
            txtStatus.append("Exception Error : " + e + "\n");
            RetValue = false;
        }

        try {
            lfhfratio = Double.valueOf(txtLfhfratio.getText()).doubleValue();
        } catch(java.lang.NumberFormatException e){
            txtStatus.append("Incorrect 'LF/HF ratio' entered, please correct it!\n");
            txtStatus.append("Exception Error : " + e + "\n");
            RetValue = false;
        }

        /*
         * ECG morphology: Order of extrema: [P Q R S T]
         */
        // theta
        try {
            theta[1] = Double.valueOf(tiTable.getValueAt(0,0).toString()).doubleValue();
        } catch(java.lang.NumberFormatException e){
            txtStatus.append("Incorrect 'theta' value entered (position 1), please correct it!\n");
            txtStatus.append("Exception Error : " + e + "\n");
            RetValue = false;
        }
        try {
            theta[2] = Double.valueOf(tiTable.getValueAt(1,0).toString()).doubleValue();
        } catch(java.lang.NumberFormatException e){
            txtStatus.append("Incorrect 'theta' value entered (position 2), please correct it!\n");
            txtStatus.append("Exception Error : " + e + "\n");
            RetValue = false;
        }
        try {
            theta[3] = Double.valueOf(tiTable.getValueAt(2,0).toString()).doubleValue();
        } catch(java.lang.NumberFormatException e){
            txtStatus.append("Incorrect 'theta' value entered (position 3), please correct it!\n");
            txtStatus.append("Exception Error : " + e + "\n");
            RetValue = false;
        }
        try {
            theta[4] = Double.valueOf(tiTable.getValueAt(3,0).toString()).doubleValue();
        } catch(java.lang.NumberFormatException e){
            txtStatus.append("Incorrect 'theta' value entered (position 4), please correct it!\n");
            txtStatus.append("Exception Error : " + e + "\n");
            RetValue = false;
        }
        try {
            theta[5] = Double.valueOf(tiTable.getValueAt(4,0).toString()).doubleValue();
        } catch(java.lang.NumberFormatException e){
            txtStatus.append("Incorrect 'theta' value entered (position 5), please correct it!\n");
            txtStatus.append("Exception Error : " + e + "\n");
            RetValue = false;
        }

        // a
        try {
            a[1] = Double.valueOf(aiTable.getValueAt(0,0).toString()).doubleValue();
        } catch(java.lang.NumberFormatException e){
            txtStatus.append("Incorrect 'a' value entered (position 1), please correct it!\n");
            txtStatus.append("Exception Error : " + e + "\n");
            RetValue = false;
        }
        try {
            a[2] = Double.valueOf(aiTable.getValueAt(1,0).toString()).doubleValue();
        } catch(java.lang.NumberFormatException e){
            txtStatus.append("Incorrect 'a' value entered (position 2), please correct it!\n");
            txtStatus.append("Exception Error : " + e + "\n");
            RetValue = false;
        }
        try {
            a[3] = Double.valueOf(aiTable.getValueAt(2,0).toString()).doubleValue();
        } catch(java.lang.NumberFormatException e){
            txtStatus.append("Incorrect 'a' value entered (position 3), please correct it!\n");
            txtStatus.append("Exception Error : " + e + "\n");
            RetValue = false;
        }
        try {
            a[4] = Double.valueOf(aiTable.getValueAt(3,0).toString()).doubleValue();
        } catch(java.lang.NumberFormatException e){
            txtStatus.append("Incorrect 'a' value entered (position 4), please correct it!\n");
            txtStatus.append("Exception Error : " + e + "\n");
            RetValue = false;
        }
        try {
            a[5] = Double.valueOf(aiTable.getValueAt(4,0).toString()).doubleValue();
        } catch(java.lang.NumberFormatException e){
            txtStatus.append("Incorrect 'a' value entered (position 5), please correct it!\n");
            txtStatus.append("Exception Error : " + e + "\n");
            RetValue = false;
        }

        // b
        try {
            b[1] = Double.valueOf(biTable.getValueAt(0,0).toString()).doubleValue();
        } catch(java.lang.NumberFormatException e){
            txtStatus.append("Incorrect 'b' value entered (position 1), please correct it!\n");
            txtStatus.append("Exception Error : " + e + "\n");
            RetValue = false;
        }
        try {
            b[2] = Double.valueOf(biTable.getValueAt(1,0).toString()).doubleValue();
        } catch(java.lang.NumberFormatException e){
            txtStatus.append("Incorrect 'b' value entered (position 2), please correct it!\n");
            txtStatus.append("Exception Error : " + e + "\n");
            RetValue = false;
        }
        try {
            b[3] = Double.valueOf(biTable.getValueAt(2,0).toString()).doubleValue();
        } catch(java.lang.NumberFormatException e){
            txtStatus.append("Incorrect 'b' value entered (position 3), please correct it!\n");
            txtStatus.append("Exception Error : " + e + "\n");
            RetValue = false;
        }
        try {
            b[4] = Double.valueOf(biTable.getValueAt(3,0).toString()).doubleValue();
        } catch(java.lang.NumberFormatException e){
            txtStatus.append("Incorrect 'b' value entered (position 4), please correct it!\n");
            txtStatus.append("Exception Error : " + e + "\n");
            RetValue = false;
        }
        try {
            b[5] = Double.valueOf(biTable.getValueAt(4,0).toString()).doubleValue();
        } catch(java.lang.NumberFormatException e){
            txtStatus.append("Incorrect 'b' value entered (position 5), please correct it!\n");
            txtStatus.append("Exception Error : " + e + "\n");
            RetValue = false;
        }

        /*
         * ECG Animate parameters
         */
        // convert into miliseconds interval
        ecgAnimateInterval = (long)(1000/(sfecg));        

        if(RetValue){
            txtStatus.append("All parameters are valid!.\n");
        }else{
            txtStatus.append("There were errors in some parameters!.\n");
        }

        txtStatus.append("Finished checking ECG parameters.\n\n");

        return(RetValue);
    }

    /*************************
     * THE ECG FUNCTION
     **************************/
    boolean ecgFunction(){
        boolean RetValue;

        txtStatus.append("Starting to generate ECG table data....\n");
        
        ecgCalc Ecg = new ecgCalc();
        RetValue = Ecg.dorun();

        txtStatus.append("Finished generating ECG table data.\n\n");

        return(RetValue);
    }
    
    private void generateButtonActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_generateButtonActionPerformed
        desktopPane.setCursor(java.awt.Cursor.getPredefinedCursor(java.awt.Cursor.WAIT_CURSOR));
        paramDialog.hide();

        /*
         * Clear Status text.
         */
        txtStatus.setText(null);
        txtStatus.append("************************************************************\n");
        txtStatus.append("ECGSYN:\nA program for generating a realistic synthetic ECG\n\n"); 
        txtStatus.append("Copyright (c) 2003 by Patrick McSharry & Gari Clifford.\n");
        txtStatus.append("All rights reserved.\n");
        txtStatus.append("See IEEE Transactions On Biomedical Engineering, 50(3),\n289-294, March 2003.\n\n");
        txtStatus.append("Contact:\nP. McSharry ([email protected])\nG. Clifford ([email protected])\n");
        txtStatus.append("************************************************************\n\n");
        txtStatus.append("ECG process started.\n\n");       
        txtStatus.append("Starting to clear table data and widgets values....\n");

        /*
         * Set the Amplitude labels
         */
        lblMaxAmplitude.setText(txtAmplitude.getText());
        lblMinAmplitude.setText("-" + txtAmplitude.getText());

        /*
         * Re init the plot state.
         * Disable repaint for the moment, until we finish the FFT function.
         */
        readyToPlot = false;
        plotScrollBarValue = 0;
        plotScrollBar.setMaximum(0);

        /* Delete any data on the Data Table. */
        clearDataTable();

        txtStatus.append("Finished clearing table data and widgets values.\n\n");
        /*
         * Call the ECG funtion to calculate the data into the Data Table.
         */
        if(ecgFunction()){

            txtStatus.append("Starting to plot ECG table data....\n");
            
            /*
            * if the # Data Table rows is less than the ecgFrame width, we do not
            * need the scrollbar
            */
            int rows = tableValuesModel.getRowCount();
            if(rows > ecgFrame.getBounds().width){
                plotScrollBar.setMaximum(rows - ecgFrame.getBounds().width - 1); 
            }

            /*
            * Only plot if there's data in the table.
            */
            if(rows > 0){
                readyToPlot = true;
                ecgGenerated = true;
                enableButtons();
            }else{
                txtStatus.append("No data to plot!.\n");
            }

            ecgFrame.repaint();
            txtStatus.append("Finished plotting ECG table data.\n\n");

        }
        txtStatus.append("Finsihed ECG process.\n");
        txtStatus.append("************************************************************\n");

        desktopPane.setCursor(java.awt.Cursor.getPredefinedCursor(java.awt.Cursor.DEFAULT_CURSOR));
    }//GEN-LAST:event_generateButtonActionPerformed

    // Variables declaration - do not modify//GEN-BEGIN:variables
    private javax.swing.JScrollPane ExtremaLabelScrollPane;
    private javax.swing.JTable ExtremaLabelTable;
    private javax.swing.JScrollPane TableScrollPane;
    private javax.swing.JScrollPane aiScrollPane;
    private javax.swing.JTable aiTable;
    private javax.swing.JDialog alert;
    private javax.swing.JButton alertButton;
    private javax.swing.JDesktopPane alertDesktopPane;
    private javax.swing.JTextArea alertText;
    private javax.swing.JLabel alertTitle;
    private javax.swing.JDesktopPane animateDesktopPane;
    private javax.swing.JScrollPane biScrollPane;
    private javax.swing.JTable biTable;
    private javax.swing.JDesktopPane calculateDesktopPane;
    private javax.swing.JButton clearButton;
    private javax.swing.JButton closeParamDialogButton;
    private javax.swing.JDesktopPane desktopPane;
    private javax.swing.JScrollPane ecgPlotArea;
    private javax.swing.JInternalFrame ecgWindow;
    private javax.swing.JButton exportButton;
    private javax.swing.JDialog exportDialog;
    private javax.swing.JPanel extremaPanel;
    private javax.swing.JPanel generalInterfacePanel;
    private javax.swing.JButton generateButton;
    private javax.swing.JDialog helpDialog;
    private javax.swing.JEditorPane helpEditorPane;
    private javax.swing.JInternalFrame helpInternalFrame;
    private javax.swing.JScrollPane helpScrollPane;
    private javax.swing.JLabel jLabel1;
    private javax.swing.JLabel jLabel2;
    private javax.swing.JLabel jLabel3;
    private javax.swing.JLabel jLabel4;
    private javax.swing.JLabel jLabel5;
    private javax.swing.JLabel jLabel6;
    private javax.swing.JLabel jLabel7;
    private javax.swing.JLabel lblAmplitude;
    private javax.swing.JLabel lblAnoise;
    private javax.swing.JLabel lblFhi;
    private javax.swing.JLabel lblFhistd;
    private javax.swing.JLabel lblFlo;
    private javax.swing.JLabel lblFlostd;
    private javax.swing.JLabel lblGeneralTitle;
    private javax.swing.JLabel lblHrmean;
    private javax.swing.JLabel lblHrstd;
    private javax.swing.JLabel lblLfhfratio;
    private javax.swing.JLabel lblMaxAmplitude;
    private javax.swing.JLabel lblMinAmplitude;
    private javax.swing.JLabel lblMorphologyTitle;
    private javax.swing.JLabel lblN;
    private javax.swing.JLabel lblOrigin;
    private javax.swing.JLabel lblSeed;
    private javax.swing.JLabel lblSf;
    private javax.swing.JLabel lblSfecg;
    private javax.swing.JLabel lblSpectralTitle;
    private javax.swing.JLabel lblXAxis;
    private javax.swing.JButton paramButton;
    private javax.swing.JDesktopPane paramDesktopPane;
    private javax.swing.JDialog paramDialog;
    private javax.swing.JButton paramHelpButton;
    private javax.swing.JTabbedPane paramTabbedPane;
    private javax.swing.JScrollBar plotScrollBar;
    private javax.swing.JButton resetParamDialogButton;
    private javax.swing.JButton saveParamDialogButton;
    private javax.swing.JPanel spectralCharacteristicsPanel;
    private javax.swing.JButton startAnimateButton;
    private javax.swing.JScrollPane statusScrollPane;
    private javax.swing.JButton stopAnimateButton;
    private javax.swing.JTable tableValues;
    private javax.swing.JScrollPane tiScrollPane;
    private javax.swing.JTable tiTable;
    private javax.swing.JTextField txtAmplitude;
    private javax.swing.JTextField txtAnoise;
    private javax.swing.JTextField txtFhi;
    private javax.swing.JTextField txtFhistd;
    private javax.swing.JTextField txtFlo;
    private javax.swing.JTextField txtFlostd;
    private javax.swing.JTextField txtHrmean;
    private javax.swing.JTextField txtHrstd;
    private javax.swing.JTextField txtLfhfratio;
    private javax.swing.JTextField txtN;
    private javax.swing.JTextField txtSeed;
    private javax.swing.JTextField txtSf;
    private javax.swing.JTextField txtSfecg;
    private javax.swing.JTextArea txtStatus;
    private javax.swing.JDesktopPane zommDesktopPane;
    private javax.swing.JButton zoomInButton;
    private javax.swing.JButton zoomOutButton;
    // End of variables declaration//GEN-END:variables

    private javax.swing.table.DefaultTableModel tableValuesModel;
    
    private ecgPanel ecgFrame;

    /*
    * This class is the AdjustmentListener for the
    * scroll bar. So the events come here when the
    * scroll bar is moved.
    */
    public void adjustmentValueChanged(AdjustmentEvent evt){
    plotScrollBarValue = plotScrollBar.getValue();
    ecgFrame.repaint();
    }

    class ecgPanel extends javax.swing.JPanel{

        public void paintComponent(Graphics g){
            // First call the paintComponent of the
            // superclass, in this case JPanel.
            super.paintComponent(g);

            /* Draw the plot frame. */
            g.setColor(frameLineColor);
            g.drawLine(0, posFrameY, ecgFrame.getBounds().width, posFrameY);
            g.drawLine(0, posOriginY, this.getBounds().width, posOriginY);
            g.drawLine(0, horzScaleY, this.getBounds().width, horzScaleY);

            if(readyToPlot){
                int rows = tableValuesModel.getRowCount();
                int x, y, i;
                int plotLimit;
                int initialZero;
                int curSecond, lastSecond;
                String strValue;

                /*
                 * Set the first point to the current Table row
                 */
                initialZero =  (int)(Double.valueOf(tableValues.getValueAt(plotScrollBarValue, 0).toString()).doubleValue() / plotZoom);
                lastSecond = (int)(Double.valueOf(tableValues.getValueAt(plotScrollBarValue, 0).toString()).doubleValue());
                x = 0;
                y = posOriginY - (int)(Double.valueOf(tableValues.getValueAt(plotScrollBarValue, 1).toString()).doubleValue() * frameAmplitude / amplitude);
                Point lastPoint = new java.awt.Point(x, y);
                i= plotScrollBarValue;

                while((x <= this.getBounds().width)&& (i <=rows)){
                    curSecond = (int)(Double.valueOf(tableValues.getValueAt(i, 0).toString()).doubleValue());
                    if(curSecond > lastSecond){
                        lastSecond = curSecond;
                        // Convert the x value to a string
                        strValue = FormatNumber.toString(Double.valueOf(tableValues.getValueAt(i, 0).toString()).doubleValue(), upLimit, loLimit, 2);
                        /*
                         * Plot the X axes number values (the Time).
                         */
                        g.setColor(axesNumColor);
                        drawText(g, strValue,
                        x, horzScaleY, horzScaleWidth, horzScaleHeight,
                        fScaleNumSize,LEFT);
                        g.setColor(frameInsideLineColor);
                        g.drawLine(x, posFrameY, x, horzScaleY + 5);
                    }

                    /*
                     * Plot a line between the las point and the current point.
                     * This to create a illusion to connect the two points.
                     */
                    g.setColor(ecgPlotColor);
                    g.drawLine(lastPoint.x, lastPoint.y, x, y);

                    /*
                     * Set the current point to be the last, and 
                     * get a new point to plot in the following loop.
                     */
                    lastPoint.setLocation(x, y);
                    i+= 1;
                    x = (int)(Double.valueOf(tableValues.getValueAt(i, 0).toString()).doubleValue() / plotZoom) - initialZero;
                    y = posOriginY - (int)(Double.valueOf(tableValues.getValueAt(i, 1).toString()).doubleValue() * frameAmplitude / amplitude);
                }
            }
        }
    }

    class ecgCalc{

        /* C defines */
        private final double PI = 2.0*Math.asin(1.0);
        private final int NR_END = 1;
        private final int IA = 16807;
        private final long IM = 2147483647;
        private final double AM = (1.0/IM);
        private final long IQ = 127773;
        private final int IR = 2836;
        private final int NTAB = 32;
        private final double NDIV = (1+(IM-1)/NTAB);
        private final double EPS = 1.2e-7;
        private final double RNMX = (1.0-EPS);

        /*****************************************************************************
         *    DEFINE PARAMETERS AS GLOBAL VARIABLES                                 *
         *****************************************************************************/
        private String outfile ="ecgsyn.dat";
        // Order of extrema: [P Q R S T]
        private double[] ti = new double[6];  /* ti converted in radians             */
        private double[] ai = new double[6];  /* new calculated a                    */
        private double[] bi = new double[6];  /* new calculated b                    */        

        private int Necg = 0;                 /*  Number of ECG outputs              */
        private int mstate = 3;               /*  System state space dimension       */
        private double xinitial = 1.0;        /*  Initial x co-ordinate value        */
        private double yinitial = 0.0;        /*  Initial y co-ordinate value        */
        private double zinitial = 0.04;       /*  Initial z co-ordinate value        */
        private long rseed; 
        private double h; 
        private double[] rr, rrpc;

        /*
         * Variables for static function rand()
         */
        private long iy;
        private long[] iv;

        public ecgCalc(){
            /* variables for static function ranq() */
            iy=0;
            iv = new long[NTAB];                
        }

        /*--------------------------------------------------------------------------*/
        /*    UNIFORM DEVIATES                                                      */
        /*--------------------------------------------------------------------------*/

        private double rand(){

            int j;
            long k;
            double temp;
            boolean flg;

            if(iy == 0)
                flg = false;
            else
                flg = true;

            if((rseed <= 0) || !flg){
                if (-(rseed) < 1)
                    rseed = 1;
                else
                    rseed = -rseed;

                for (j=NTAB+7; j>=0; j--) {
                        k=(rseed)/IQ;
                        rseed=IA*(rseed-k*IQ)-IR*k;
                        if (rseed < 0)
                            rseed += IM;
                        if (j < NTAB)
                            iv[j] = rseed;
                }
                iy=iv[0];
            }

            k=(rseed)/IQ;
            rseed=IA*(rseed-k*IQ)-IR*k;
            if (rseed< 0)
                rseed += IM;

            j = (int)(iy/NDIV);
            iy=iv[j];
            iv[j] = rseed;

            if ((temp=AM*iy) > RNMX)
                return RNMX;
            else
                return temp;
        }

        /*
         * FFT
         */
        private void ifft(double[] data, long nn, int isign){

            long n, mmax, m, istep, i, j;
            double wtemp,wr,wpr,wpi,wi,theta;
            double tempr,tempi;
            double swap;

            n=nn << 1;
            j=1;
            for (i=1; i< n; i+=2) {
                    if (j > i) {
                        //SWAP(data[j],data[i]);
                        swap = data[(int) j];
                        data[(int)j] = data[(int)i];
                        data[(int)i] = swap;
                        //SWAP(data[j+1],data[i+1]);
                        swap = data[(int)j+1];
                        data[(int)j+1] = data[(int)i+1];
                        data[(int)i+1] = swap;
                    }
                    m=n >> 1;
                    while (m >= 2 && j > m) {
                            j -= m;
                            m >>= 1;
                    }
                    j += m;
            }
            mmax=2;
            while (n > mmax) {
                    istep=mmax << 1;
                    theta=isign*(6.28318530717959/mmax);
                    wtemp=Math.sin(0.5*theta);
                    wpr = -2.0*wtemp*wtemp;
                    wpi=Math.sin(theta);
                    wr=1.0;
                    wi=0.0;
                    for (m=1; m<mmax; m+=2) {
                            for (i=m; i<=n; i+=istep) {
                                    j= i + mmax;
                                    tempr=wr * data[(int)j] - wi * data[(int)j+1];
                                    tempi=wr * data[(int)j+1] + wi * data[(int)j];
                                    data[(int)j]= data[(int)i] - tempr;
                                    data[(int)j+1]= data[(int)i+1] - tempi;
                                    data[(int)i] += tempr;
                                    data[(int)i+1] += tempi;
                            }
                            wr=(wtemp=wr)*wpr-wi*wpi+wr;
                            wi=wi*wpr+wtemp*wpi+wi;
                    }
                    mmax=istep;
            }
        }

        /*
         * STANDARD DEVIATION CALCULATOR
         */
        /* n-by-1 vector, calculate standard deviation */
        private double stdev(double[] x, int n){
                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 (Math.sqrt(total/((double)n-1)));
        }        

        /*
         * THE ANGULAR FREQUENCY
         */
        private double angfreq(double t){
           int i = 1 + (int)Math.floor(t/h);
           return(2.0*PI/rrpc[i]);
        }

        /*--------------------------------------------------------------------------*/
        /*    THE EXACT NONLINEAR DERIVATIVES                                       */
        /*--------------------------------------------------------------------------*/
        private void derivspqrst(double t0,double[] x, double[] dxdt){

            int i,k;
            double a0,w0,r0,x0,y0,z0;
            double t,dt,dt2,zbase;
            double[] xi, yi;

            k = 5; 
            xi = new double[k + 1];
            yi = new double[k + 1];
            w0 = angfreq(t0);
            r0 = 1.0; x0 = 0.0;  y0 = 0.0;  z0 = 0.0;
            a0 = 1.0 - Math.sqrt((x[1]-x0)*(x[1]-x0) + (x[2]-y0)*(x[2]-y0))/r0;

            for(i=1; i<=k; i++)
                xi[i] = Math.cos(ti[i]);
            for(i=1; i<=k; i++)
                yi[i] = Math.sin(ti[i]);   


            zbase = 0.005* Math.sin(2.0*PI*fhi*t0);

            t = Math.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 = Math.IEEEremainder(t-ti[i], 2.0*PI);
              dt2 = dt*dt;
              dxdt[3] += -ai[i] * dt * Math.exp(-0.5*dt2/(bi[i]*bi[i])); 
            }
            dxdt[3] += -1.0*(x[3] - zbase);
        }

        /*
         * RUNGA-KUTTA FOURTH ORDER INTEGRATION
         */
        private void Rk4(double[] y, int n, double x, double h, double[] yout){
            int i;
            double xh,hh,h6;
            double[] dydx, dym, dyt, yt;

            dydx=  new double[n + 1];
            dym =  new double[n + 1];
            dyt =  new double[n + 1];
            yt  =  new double[n + 1];

            hh= h * 0.5;
            h6= h/6.0;
            xh= x + hh;

            derivspqrst(x,y,dydx);
            for (i=1; i<=n; i++)
                yt[i]=y[i]+hh*dydx[i];

            derivspqrst(xh,yt,dyt);
            for (i=1; i<=n; i++)
                yt[i]=y[i] + hh * dyt[i];

            derivspqrst(xh,yt,dym);
            for (i=1; i<=n; i++){
                    yt[i]=y[i] + h * dym[i];
                    dym[i] += dyt[i];
            }

            derivspqrst(x+h,yt,dyt);
            for (i=1; i<=n; i++)
                    yout[i]=y[i] + h6 * (dydx[i]+dyt[i]+2.0*dym[i]);
        }

        /*
         * GENERATE RR PROCESS
         */
        private 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;
            double[] w, Hw, Sw, ph0, ph, SwC;

            w =  new double[n+1];
            Hw = new double[n+1];
            Sw = new double[n+1];
            ph0= new double[(int)(n/2-1 +1)];
            ph = new double[n+1];
            SwC= new double[(2*n)+1];

            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/(double)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*Math.exp(-0.5*(Math.pow(w[i]-w1,2)/Math.pow(c1,2))) / Math.sqrt(2*PI*c1*c1))
                    + (sig2*Math.exp(-0.5*(Math.pow(w[i]-w2,2)/Math.pow(c2,2))) / Math.sqrt(2*PI*c2*c2));
            }

            for(i=1; i<=n/2; i++)
               Sw[i] = (sf/2.0)* Math.sqrt(Hw[i]);

            for(i=n/2+1; i<=n; i++)
               Sw[i] = (sf/2.0)* Math.sqrt(Hw[n-i+1]);

            /* randomise the phases */
            for(i=1; i<=n/2-1; i++)
               ph0[i] = 2.0*PI*rand();

            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]* Math.cos(ph[i]);

            for(i=1; i<=n; i++)
               SwC[2*i] = Sw[i]* Math.sin(ph[i]);

            /* calculate inverse fft */
            ifft(SwC,n,-1);

            /* extract real part */
            for(i=1; i<=n; i++)
               rr[i] = (1.0/(double)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;

        }

        /*
         * DETECT PEAKS
         */
        private void 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;

            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 = Math.atan2(y[1],x[1]);

            for(i=1; i<n; i++){
              theta2 = Math.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)Math.ceil(sfecg/64);
            for(i=1; i<=n; i++){ 
                if( ipeak[i]==1 || ipeak[i]==3 || ipeak[i]==5 ){

                    j1 = (1 > (i-d) ? 1 : (i-d)); //MAX(1,i-d);
                    j2 = (n < (i+d) ? n : (i+d)); //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 = (1 > (i-d) ? 1 : (i-d));//MAX(1,i-d);
                    j2 = (n < (i+d) ? n : (i+d)); //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;
                    }
                }
            }
        }

        /*
         * DORUN PART OF PROGRAM
         */
        public boolean dorun(){

            boolean RetValue = true;

            int i, j, k, Nrr, Nt, Nts;
            int q;
            double[] x;
            double tstep, tecg, rrmean, hrfact, hrfact2;
            double qd;
            double[] xt, yt, zt, xts, yts, zts;
            double timev, zmin, zmax, zrange;
            double[] ipeak;

            // perform some checks on input values
            q = (int) Math.rint(sf/sfecg);
            qd = (double)sf/(double)sfecg;

            /* convert angles from degrees to radians and copy a vector to ai*/
            for(i=1; i <= 5; i++){
                ti[i] = theta[i] * PI/180.0;
                ai[i] = a[i];
            }

            /* adjust extrema parameters for mean heart rate */
            hrfact =  Math.sqrt(hrmean/60);
            hrfact2 = Math.sqrt(hrfact);

            for(i=1; i <= 5; i++)
               bi[i] = b[i] * hrfact;

            ti[1] *= hrfact2;
            ti[2] *= hrfact;
            ti[3] *= 1.0;
            ti[4] *= hrfact;
            ti[5] *= 1.0;

            /* declare state vector */
            //x=dvector(1,mstate);
            x= new double[4];

            txtStatus.append("Approximate number of heart beats: " + N +"\n");
            txtStatus.append("ECG sampling frequency: " + sfecg + " Hertz\n");
            txtStatus.append("Internal sampling frequency: " + sf + " Hertz\n");
            txtStatus.append("Amplitude of additive uniformly distributed noise: " + Anoise + " mV\n");
            txtStatus.append("Heart rate mean: " + hrmean + " beats per minute\n");
            txtStatus.append("Heart rate std: " + hrstd + " beats per minute\n");
            txtStatus.append("Low frequency: " + flo + " Hertz\n");
            txtStatus.append("High frequency std: " + fhistd + " Hertz\n");
            txtStatus.append("Low frequency std: " + flostd + " Hertz\n");
            txtStatus.append("High frequency: " + fhi + " Hertz\n");
            txtStatus.append("LF/HF ratio: " + lfhfratio + "\n");
            txtStatus.append("time step milliseconds: " + ecgAnimateInterval + "\n\n");
            txtStatus.append("Order of Extrema:\n");
            txtStatus.append("      theta(radians)\n");
            txtStatus.append("P: ["+ ti[1] + "\t]\n");
            txtStatus.append("Q: ["+ ti[2] + "\t]\n");
            txtStatus.append("R: ["+ ti[3] + "\t]\n");
            txtStatus.append("S: ["+ ti[4] + "\t]\n");
            txtStatus.append("T: ["+ ti[5] + "\t]\n\n");
            txtStatus.append("      a(calculated)\n");
            txtStatus.append("P: ["+ ai[1] + "\t]\n");
            txtStatus.append("Q: ["+ ai[2] + "\t]\n");
            txtStatus.append("R: ["+ ai[3] + "\t]\n");
            txtStatus.append("S: ["+ ai[4] + "\t]\n");
            txtStatus.append("T: ["+ ai[5] + "\t]\n\n");
            txtStatus.append("      b(calculated)\n");
            txtStatus.append("P: ["+ bi[1] + "\t]\n");
            txtStatus.append("Q: ["+ bi[2] + "\t]\n");
            txtStatus.append("R: ["+ bi[3] + "\t]\n");
            txtStatus.append("S: ["+ bi[4] + "\t]\n");
            txtStatus.append("T: ["+ bi[5] + "\t]\n\n");

            /* Initialise the vector */
            x[1] = xinitial; 
            x[2] = yinitial;
            x[3] = zinitial;

            /* initialise seed */
            rseed = -seed;

            /* calculate time scales */
            h = 1.0/(double)sf;
            tstep = 1.0/(double)sfecg;

            /* calculate length of RR time series */            
            rrmean = (60.0/hrmean);
            Nrr=(int)Math.pow(2.0, Math.ceil(Math.log(N*rrmean*sf)/Math.log(2.0))); 

            txtStatus.append("Using " + Nrr + " = 2^ "+ (int)(Math.log(1.0*Nrr)/Math.log(2.0)) + " samples for calculating RR intervals\n");

            /* create rrprocess with required spectrum */
            rr = new double[Nrr + 1];
            rrprocess(rr, flo, fhi, flostd, fhistd, lfhfratio, hrmean, hrstd, sf, Nrr); 

            /* create piecewise constant rr */
            rrpc = new double[(2*Nrr) + 1];
            tecg = 0.0;
            i = 1;
            j = 1;
            while(i <= Nrr){  
              tecg += rr[j];
              j = (int) Math.rint(tecg/h);
              for(k=i; k<=j; k++)
                  rrpc[k] = rr[i];
              i = j+1;
            }
            Nt = j;

            /* integrate dynamical system using fourth order Runge-Kutta*/
            xt = new double[Nt + 1];
            yt = new double[Nt + 1];
            zt = new double[Nt + 1];
            timev = 0.0;
            for(i=1; i<=Nt; i++){
                xt[i] = x[1];
                yt[i] = x[2];
                zt[i] = x[3];
                Rk4(x, mstate, timev, h, x);
                timev += h;
            }

            /* downsample to ECG sampling frequency */
            xts = new double[Nt + 1];
            yts = new double[Nt + 1];
            zts = new double[Nt + 1];

            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 = new double[Nts + 1];
            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*rand() - 1.0);

            /*
             * insert into the ECG data table
             */
            txtStatus.append("Inserting rows...\n");
            for(i=1;i<=Nts;i++){
                //fprintf(fp,"%f %f %d\n",(i-1)*tstep,zts[i],(int)ipeak[i]);
                Vector nuevoRow = new Vector(3);
                nuevoRow.addElement(new String(Double.toString((i-1)*tstep)));
                nuevoRow.addElement(new String(Double.toString(zts[i])));
                nuevoRow.addElement(new String(Integer.toString((int)ipeak[i])));
                tableValuesModel.addRow(nuevoRow);                
            }

            txtStatus.append("Finished inserting (" + i + ") rows.\n");

            //RetValue = false;

            return(RetValue);
        }
    }

    /*
     * Class to plot the ECG animation
     */
    class ECGAnimate extends TimerTask{

        int x = 0;
        int y = posOriginY - (int)(Double.valueOf(tableValues.getValueAt(ecgAnimateCurRow, 1).toString()).doubleValue() * frameAmplitude / amplitude);
        int curSecond = 0;
        int lastSecond = 0;
        Graphics ga = ecgFrame.getGraphics();

        public void run(){
            curSecond = (int)(Double.valueOf(tableValues.getValueAt(ecgAnimateCurRow, 0).toString()).doubleValue());
            if(curSecond > lastSecond){
                lastSecond = curSecond;
                /*
                 * Plot the X axes number values (the Time).
                 */
                ga.setColor(axesNumColor);
                drawText(ga, FormatNumber.toString(Double.valueOf(tableValues.getValueAt(ecgAnimateCurRow, 0).toString()).doubleValue(), upLimit, loLimit, 2),
                x, horzScaleY, horzScaleWidth, horzScaleHeight,
                fScaleNumSize,LEFT);

                ga.setColor(frameInsideLineColor);
                ga.drawLine(x, posFrameY, x, horzScaleY + 5);

            }

            ga.setColor(ecgPlotColor);
            ga.drawLine(ecgAnimateLastPoint.x, ecgAnimateLastPoint.y, x, y);
            ecgAnimateCurRow += 1;

            if(ecgAnimateCurRow >= ecgAnimateNumRows){
                /*
                 * If we reach the end of the Data Table, loop again entire table.
                 */
                ecgFrame.repaint();
                ecgAnimateCurRow = 0;
                ecgAnimateInitialZero = 0;
                x = 0;
                y = posOriginY - (int)(Double.valueOf(tableValues.getValueAt(ecgAnimateCurRow, 1).toString()).doubleValue() * frameAmplitude / amplitude);
                ecgAnimateLastPoint.setLocation(x, y);
                curSecond  = 0;
                lastSecond = 0;

            } else if(x > ecgAnimatePanelWidth){
                /*
                 * If we not reached the end of the Data Table, but we reach to the limit of
                 * the Plot Area. so reset the X coordinate to begin again.
                 */
                ecgFrame.repaint();
                x = 0;
                y = posOriginY - (int)(Double.valueOf(tableValues.getValueAt(ecgAnimateCurRow, 1).toString()).doubleValue() * frameAmplitude / amplitude);
                ecgAnimateInitialZero = (int)(Double.valueOf(tableValues.getValueAt(ecgAnimateCurRow, 0).toString()).doubleValue() / plotZoom);
                ecgAnimateLastPoint.setLocation(x, y);
                //curSecond  = 0;
                //lastSecond = 0;
            } else{
                ecgAnimateLastPoint.setLocation(x, y);
                x = (int)(Double.valueOf(tableValues.getValueAt(ecgAnimateCurRow, 0).toString()).doubleValue() / plotZoom) - ecgAnimateInitialZero;
                y = posOriginY - (int)(Double.valueOf(tableValues.getValueAt(ecgAnimateCurRow, 1).toString()).doubleValue() * frameAmplitude / amplitude);                
            }
        }
    }

    /*
     * class for verifying that the value of JtextField is a double number
     */
    class doubleVerifier extends InputVerifier{
        
        public boolean verify(JComponent input) {
            JTextField textF = (JTextField) input;
            double dNumber;
            boolean RetValue = true;

            try {
                dNumber = Double.valueOf(textF.getText()).doubleValue();
            } catch(java.lang.NumberFormatException e){
                getToolkit().beep();
                alert.setTitle("Error!!!");
                alertText.setText("You have to enter a double number, please retry!!!");
                alert.show();
                RetValue = false;
            }
            return(RetValue);
        }
    }

    /*
     * class for verifying that the value of JtextField is an integer number
     */    
    class integerVerifier extends InputVerifier{
        
        public boolean verify(JComponent input) {
            JTextField textF = (JTextField) input;
            int dNumber;
            boolean RetValue = true;

            try {
                dNumber = Integer.valueOf(textF.getText()).intValue();
            } catch(java.lang.NumberFormatException e){
                getToolkit().beep();
                alert.setTitle("Error!!!");
                alertText.setText("You have to enter an integer number, please retry!!!");
                alert.show();
                RetValue = false;
            }
            return(RetValue);
        }
    }

    /*
     * class for verifying the value of the ECG Sampling frequency
     */    
    class sfecgVerifier extends InputVerifier{
        
        public boolean verify(JComponent input) {
            JTextField textF = (JTextField) input;
            int dNumber;
            boolean RetValue = true;

            try {
                dNumber = Integer.valueOf(textF.getText()).intValue();
                txtSf.setText(Integer.toString(dNumber));
            } catch(java.lang.NumberFormatException e){
                getToolkit().beep();
                alert.setTitle("Error!!!");
                alertText.setText("You have to enter an integer number, please retry!!!");
                alert.show();
                RetValue = false;
            }
            return(RetValue);
        }
    }

    /*
     * class for verifying the value of the Internal Sampling frequency
     */    
    class sfVerifier extends InputVerifier{
        
        public boolean verify(JComponent input) {
            JTextField textF = (JTextField) input;
            int dNumber;
            boolean RetValue = true;

            try {
                dNumber = Integer.valueOf(textF.getText()).intValue();
                int sfecgTemp;
                sfecgTemp = Integer.valueOf(txtSfecg.getText()).intValue();
                if(((int)Math.IEEEremainder(dNumber, sfecgTemp)) != 0){
                    getToolkit().beep();
                    alert.setTitle("Error!!!");
                    alertText.setText("Internal sampling frequency must be an integer multiple of the ECG sampling frequency, please retry!");
                    alert.show();
                    RetValue = false;
                }
            } catch(java.lang.NumberFormatException e){
                getToolkit().beep();
                alert.setTitle("Error!!!");
                alertText.setText("You have to enter an integer number, please retry!!!");
                alert.show();
                RetValue = false;
            }
            return(RetValue);
        }
    }
}