/*
* Copyright 2008, 2012 Free Software Foundation, Inc.
* Copyright 2013 Alexander Chemeris <Alexander.Chemeris@fairwaves.ru>
*
* This software is distributed under the terms of the GNU Public License.
* See the COPYING file in the main directory for details.
*
* This use of this software may be subject to additional restrictions.
* See the LEGAL file in the main directory for details.

    This program is free software: you can redistribute it and/or modify
    it under the terms of the GNU General Public License as published by
    the Free Software Foundation, either version 3 of the License, or
    (at your option) any later version.

    This program is distributed in the hope that it will be useful,
    but WITHOUT ANY WARRANTY; without even the implied warranty of
    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
    GNU General Public License for more details.

    You should have received a copy of the GNU General Public License
    along with this program.  If not, see <http://www.gnu.org/licenses/>.

*/



/*
	Compilation switches
	TRANSMIT_LOGGING	write every burst on the given slot to a log
*/

#ifndef _DRIVELOOP_H_
#define _DRIVELOOP_H_

#include "radioInterface.h"
#include "Interthread.h"
#include "GSMCommon.h"
#include "Sockets.h"

#include <sys/types.h>
#include <sys/socket.h>

/** Define this to be the slot number to be logged. */
//#define TRANSMIT_LOGGING 1

/** The Transceiver class, responsible for physical layer of basestation */
class DriveLoop : public Thread {
  
private:

  GSM::Time mTransmitLatency;     ///< latency between basestation clock and transmit deadline clock
  GSM::Time mLatencyUpdateTime;   ///< last time latency was updated
  GSM::Time mLastClockUpdateTime; ///< last time clock update was sent up to core

  UDPSocket mClockSocket;	  ///< socket for writing clock updates to GSM core

  VectorQueue  mTransmitPriorityQueue[CHAN_MAX];   ///< priority queue of transmit bursts received from GSM core

  GSM::Time mTransmitDeadlineClock;       ///< deadline for pushing bursts into transmit FIFO 
  GSM::Time mStartTime;                   ///< random start time of the radio clock

  RadioInterface *mRadioInterface;	  ///< associated radioInterface object
  double txFullScale;                     ///< full scale input to radio
  double rxFullScale;                     ///< full scale output to radio

  /** Number of channels supported by the channelizer */
  int mChanM;

  /** unmodulate a modulated burst */
#ifdef TRANSMIT_LOGGING
  void unModulateVector(signalVector wVector); 
#endif

  /** Push modulated burst into transmit FIFO corresponding to a particular timestamp */
  void pushRadioVector(GSM::Time &nowTime);

  /** Pull and demodulate a burst from the receive FIFO */ 
  SoftVector *pullRadioVector(GSM::Time &wTime, int &RSSI, int &timingOffset);
   
  signalVector *gsmPulse;              ///< the GSM shaping pulse for modulation

  int mSamplesPerSymbol;               ///< number of samples per GSM symbol

  int fillerModulus[CHAN_MAX][8];                ///< modulus values of all timeslots, in frames
  signalVector *fillerTable[CHAN_MAX][102][8];   ///< table of modulated filler waveforms for all timeslots

  /** Channelizer path for primary ARFCN */
  int mC0;

  signalVector *mTxBursts[CHAN_MAX];
  bool         mIsFiller[CHAN_MAX];
  bool         mIsZero[CHAN_MAX];

public:

  /** Transceiver constructor 
      @param wBasePort base port number of UDP sockets
      @param TRXAddress IP address of the TRX manager, as a string
      @param wSamplesPerSymbol number of samples per GSM symbol
      @param wTransmitLatency initial setting of transmit latency
      @param radioInterface associated radioInterface object
  */
  DriveLoop(int wBasePort, const char *TRXAddress,
	    RadioInterface *wRadioInterface,
	    int wChanM = 1, int wC0 = 0,
            int wSamplesPerSymbol = SAMPSPERSYM,
	    GSM::Time wTransmitLatency = GSM::Time(3, 0));
   
  /** Destructor */
  ~DriveLoop();

  VectorQueue *priorityQueue(int m) { return &mTransmitPriorityQueue[m]; }

  /** Codes for burst types of received bursts*/
  typedef enum {
    OFF,               ///< timeslot is off
    TSC,	       ///< timeslot should contain a normal burst
    RACH,	       ///< timeslot should contain an access burst
    IDLE	       ///< timeslot is an idle (or dummy) burst
  } CorrType;

  /** Codes for channel combinations */
  typedef enum {
    FILL,               ///< Channel is transmitted, but unused
    I,                  ///< TCH/FS
    II,                 ///< TCH/HS, idle every other slot
    III,                ///< TCH/HS
    IV,                 ///< FCCH+SCH+CCCH+BCCH, uplink RACH
    V,                  ///< FCCH+SCH+CCCH+BCCH+SDCCH/4+SACCH/4, uplink RACH+SDCCH/4
    VI,                 ///< CCCH+BCCH, uplink RACH
    VII,                ///< SDCCH/8 + SACCH/8
    NONE,               ///< Channel is inactive, default
    LOOPBACK            ///< similar go VII, used in loopback testing
  } ChannelCombination;

  /** Set modulus for specific timeslot */
  void setModulus(int channel, int timeslot);

  /** return the expected burst type for the specified timestamp */
  CorrType expectedCorrType(int channel, GSM::Time currTime);

  void setTimeslot(int m, int timeslot, ChannelCombination comb)
  {
    mChanType[m][timeslot] = comb;
  }

  GSM::Time getStartTime() { return mStartTime; }
  GSM::Time getLastClockUpdate() { return mLastClockUpdateTime; }
  GSM::Time getDeadlineClock() { return mTransmitDeadlineClock; }

  /** send messages over the clock socket */
  void writeClockInterface(void);

private:

  ChannelCombination mChanType[CHAN_MAX][8];     ///< channel types for all timeslots

protected:

  /** drive reception and demodulation of GSM bursts */ 
  void driveReceiveFIFO();

  /** drive transmission of GSM bursts */
  void driveTransmitFIFO();

  /** drive handling of control messages from GSM core */
  void driveControl();

  /**
    drive modulation and sorting of GSM bursts from GSM core
    @return true if a burst was transferred successfully
  */
  bool driveTransmitPriorityQueue();

  virtual void runThread();

  void reset();

  /** set priority on current thread */
  void setPriority() { mRadioInterface->setPriority(); }

};

/** FIFO thread loop */
void *RadioDriveLoopAdapter(DriveLoop *);

#endif /* _DRIVELOOP_H_ */
