// $Id: tos-channelgen.c,v 1.4 2006-12-12 18:23:00 vlahan Exp $

/* -*- Mode: C; c-basic-indent: 2; indent-tabs-mode: nil -*- */
/*									
 *  IMPORTANT: READ BEFORE DOWNLOADING, COPYING, INSTALLING OR USING.  By
 *  downloading, copying, installing or using the software you agree to
 *  this license.  If you do not agree to this license, do not download,
 *  install, copy or use the software.
 *
 *  Intel Open Source License 
 *
 *  Copyright (c) 2002 Intel Corporation 
 *  All rights reserved. 
 *  Redistribution and use in source and binary forms, with or without
 *  modification, are permitted provided that the following conditions are
 *  met:
 * 
 *	Redistributions of source code must retain the above copyright
 *  notice, this list of conditions and the following disclaimer.
 *	Redistributions in binary form must reproduce the above copyright
 *  notice, this list of conditions and the following disclaimer in the
 *  documentation and/or other materials provided with the distribution.
 *      Neither the name of the Intel Corporation nor the names of its
 *  contributors may be used to endorse or promote products derived from
 *  this software without specific prior written permission.
 *  
 *  THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
 *  ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
 *  LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A
 *  PARTICULAR PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE INTEL OR ITS
 *  CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
 *  EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
 *  PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
 *  PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
 *  LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
 *  NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
 *  SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 * 
 * Author:	Phil Buonadonna
 * Revision:	$Id: tos-channelgen.c,v 1.4 2006-12-12 18:23:00 vlahan Exp $
 * 
 */

/*
 * This tool provides two things:
 *  1) To verify the actual channel generated by the component function
 *     CC1000Control.TuneManual(freq)  The computation engine used in
 *     this program is EXACTLY the same as it is for the on-device
 *     stack and should generate the same result.
 * 
 *  2) To generate a C-code preset-table entry for a given frequency.
 * 
 * The assumptions used are:
 *  XOSC Freq           - 14.745600 MHz
 *  Separation Freq     - 64 KHz
 *  IF                  - 150 KHz
 *  LO Injection        - High Side
 *  Baud Rate           - 38.4 Kbaud (19.2 realized w/ Manchester Encoding)
 */

#include <stdio.h>
#ifndef __CYGWIN__
#include <inttypes.h>
#endif

char gfPrintPreset = 0;

enum {
  IF = 150000,
  FREQ_MIN = 4194304,
  FREQ_MAX = 16751615
};


const uint32_t FRefTbl[9] = {2457600,
			     2106514,
			     1843200,
			     1638400,
			     1474560,
			     1340509,
			     1228800,
			     1134277,
			     1053257};

const uint16_t CorTbl[9] = {1213,
			    1416,
			    1618,
			    1820,
			    2022,
			    2224,
			    2427,
			    2629,
			    2831};

const uint16_t FSepTbl[9] = {0x1AA,
			     0x1F1,
			     0x238,
			     0x280,
			     0x2C7,
			     0x30E,
			     0x355,
			     0x39C,
			     0x3E3};

uint32_t cc1000ComputeFreq(uint32_t desiredFreq) {
  uint32_t ActualChannel = 0;
  uint32_t RXFreq = 0, TXFreq = 0;
  int32_t Offset = 0x7fffffff;
  uint16_t FSep = 0;
  uint8_t RefDiv = 0;
  uint8_t i;

  for (i = 0; i < 9; i++) {

    uint32_t NRef = ((desiredFreq + IF));
    uint32_t FRef = FRefTbl[i];
    uint32_t Channel = 0;
    uint32_t RXCalc = 0, TXCalc = 0;
    int32_t  diff;

    NRef = ((desiredFreq + IF) << 2) / FRef;
    if (NRef & 0x1) {
      NRef++;
    }

    if (NRef & 0x2) {
      RXCalc = 16384 >> 1;
      Channel = FRef >> 1;
    }

    NRef >>= 2;

    RXCalc += (NRef * 16384) - 8192;
    if ((RXCalc < FREQ_MIN) || (RXCalc > FREQ_MAX)) 
      continue;
    
    TXCalc = RXCalc - CorTbl[i];
    if ((TXCalc < FREQ_MIN) || (TXCalc > FREQ_MAX)) 
      continue;

    Channel += (NRef * FRef);
    Channel -= IF;

    diff = Channel - desiredFreq;
    if (diff < 0)
      diff = 0 - diff;

    if (diff < Offset) {
      RXFreq = RXCalc;
      TXFreq = TXCalc;
      ActualChannel = Channel;
      FSep = FSepTbl[i];
      RefDiv = i + 6;
      Offset = diff;
    }

  }

  if (RefDiv != 0) {
    uint8_t ucRxVcoCurrent, ucRxLoDrive, ucRxMatch;
    uint8_t ucTxVcoCurrent, ucTxPaDrive;
    uint8_t ucBufCurrent, ucLNACurrent; 
    if (ActualChannel < 500000000) {
      if (ActualChannel < 400000000) {
        // CURRENT (RX)
        //gCurrentParameters[0x9] = ((8 << CC1K_VCO_CURRENT) | (1 << CC1K_LO_DRIVE));
        ucRxVcoCurrent = 8; ucRxLoDrive = 1;
        // CURRENT (TX)
        //gCurrentParameters[0x1d] = ((9 << CC1K_VCO_CURRENT) | (1 << CC1K_PA_DRIVE));
        ucTxVcoCurrent = 9; ucTxPaDrive = 1;
      }
      else {
        // CURRENT (RX)
        // gCurrentParameters[0x9] = ((4 << CC1K_VCO_CURRENT) | (1 << CC1K_LO_DRIVE));
        ucRxVcoCurrent = 4; ucRxLoDrive = 1;
        // CURRENT (TX)
        // gCurrentParameters[0x1d] = ((8 << CC1K_VCO_CURRENT) | (1 << CC1K_PA_DRIVE));
        ucTxVcoCurrent = 8; ucTxPaDrive = 1;
      }
      // FRONT_END
      //gCurrentParameters[0xa] = (1 << CC1K_IF_RSSI); 
      ucBufCurrent = 0; ucLNACurrent = 0;
      // MATCH
      //gCurrentParameters[0x12] = (7 << CC1K_RX_MATCH);
      ucRxMatch = 7;
    }
    else {
      // CURRENT (RX)
      // gCurrentParameters[0x9] = ((8 << CC1K_VCO_CURRENT) | (3 << CC1K_LO_DRIVE));
      ucRxVcoCurrent = 8; ucRxLoDrive = 3;
      // CURRENT (TX)
      // gCurrentParameters[0x1d] = ((15 << CC1K_VCO_CURRENT) | (3 << CC1K_PA_DRIVE));
      ucTxVcoCurrent = 15; ucTxPaDrive = 3;
      // FRONT_END
      //gCurrentParameters[0xa] = ((1<<CC1K_BUF_CURRENT) | (2<<CC1K_LNA_CURRENT) | (1<<CC1K_IF_RSSI));
      ucBufCurrent = 1; ucLNACurrent = 2;
      // MATCH
      //gCurrentParameters[0x12] = (2 << CC1K_RX_MATCH);
      ucRxMatch = 2;
    }

    // PLL
    //gCurrentParameters[0xc] = (RefDiv << CC1K_REFDIV);

    if (!gfPrintPreset) {
      printf("\n");
      printf("DesiredFreq(Hz)\tActualFreq(Hz)\tOffset(Hz)\n");
      printf("-----------------------------------------------------------------------\n");
      printf("%d\t",desiredFreq);
      printf("%d\t",ActualChannel);
      printf("%d\n\n",Offset);
    }
    else {
      
      printf("// %f MHz Channel, 19.2 Kbps data, Manchester Encoding, High Side LO\n",
             ((float)ActualChannel/1.0e6));
      printf("{\n ");
      // MAIN
      printf("0x31,\n ");
      // FREQA
      printf("0x%x,",(uint8_t)((RXFreq >> 16) & 0xFF));  // MSB
      printf("0x%x,",(uint8_t)((RXFreq >> 8) & 0xFF));
      printf("0x%x,\n ",(uint8_t)((RXFreq) & 0xFF));  // LSB
      // FREQB
      printf("0x%x,",(uint8_t)((TXFreq >> 16) & 0xFF));  // MSB
      printf("0x%x,",(uint8_t)((TXFreq >> 8) & 0xFF));
      printf("0x%x,\n ",(uint8_t)((TXFreq) & 0xFF)); // LSB
      // FSEP
      printf("0x%x,",(uint8_t)((FSep >> 8) & 0xFF)); //MSB
      printf("0x%x,\n ",(uint8_t)((FSep) & 0xFF));  // LSB
      // CURRENT (RX)
      printf("((%d << CC1K_VCO_CURRENT) | (%d << CC1K_LO_DRIVE)), \n ",ucRxVcoCurrent,ucRxLoDrive);
      // FRONT_END
      printf("((%d<<CC1K_BUF_CURRENT) | (%d<<CC1K_LNA_CURRENT) | (1<<CC1K_IF_RSSI)), \n ",
             ucBufCurrent,ucLNACurrent);
      // PA_POW
      printf("((0x8<<CC1K_PA_HIGHPOWER) | (0x0<<CC1K_PA_LOWPOWER)),\n ");
      // PLL
      printf("((%d<<CC1K_REFDIV)),\n ",RefDiv);
      // LOCK
      printf("((0x1<<CC1K_LOCK_SELECT)),\n ");
      // CAL
      printf("((1<<CC1K_CAL_WAIT) | (6<<CC1K_CAL_ITERATE)),\n ");
      // MODEM2
      printf("((0<<CC1K_PEAKDETECT)),\n ");
      // MODEM1
      printf("((3<<CC1K_MLIMIT) | (1<<CC1K_LOCK_AVG_MODE) | (3<<CC1K_SETTLING) | (1<<CC1K_MODEM_RESET_N)),\n ");
      // MODEM0
      printf("((5<<CC1K_BAUDRATE) | (1<<CC1K_DATA_FORMAT) | (1<<CC1K_XOSC_FREQ)),\n ");
      // MATCH
      printf("((%d<<CC1K_RX_MATCH) | (0<<CC1K_TX_MATCH)),\n ",ucRxMatch);
      // FSCTRL
      printf("((1<<CC1K_FS_RESET_N)),\n ");
      // FSHAPE7 - FSHAPE1
      printf("0x00,0x00,0x00,0x00,0x00,0x00,0x00,\n ");
      // FSDELAY
      printf("0x00,\n ");
      // PRESCALER 
      printf("0x00,\n ");
      // CURRENT (TX) 
      printf("((%d << CC1K_VCO_CURRENT) | (%d << CC1K_PA_DRIVE)), \n ",ucTxVcoCurrent,ucTxPaDrive);
      // Injection Mode
      printf("TRUE\n},");
    }
  }

  //gCurrentChannel = ActualChannel;
  return ActualChannel;

}


void PrintUsage(char *ProgName) {
  printf ("Usage: %s [OPTIONS] FREQUENCY\n",ProgName);
  printf ("Determine CC1000 actual channel frequency and preset constants for\n");
  printf ("desired FREQUENCY (in Hz).\n");
  printf ("\n");
  printf ("-p\t Generate formatted C preset table entry\n");
  printf ("\n");
  printf ("Report bugs to <tinyos@millennium.berkeley.edu>\n");
}

int main(int argc, char **argv) {

  uint32_t DesiredFreq;
  uint32_t ActualFreq;
  char *szProgName = argv[0];
  char cOption;

  if (argc < 2) {
    PrintUsage(szProgName);
    return 0;
  }

  while ((--argc > 0) && ((*++argv)[0] == '-')) {
    while (cOption = *++argv[0]) {
      switch (cOption) {
      case 'p':
        gfPrintPreset = 1;
        break;
      default:
        PrintUsage(szProgName);
        return 0;
      }
    }
  }

  DesiredFreq = atoi(argv[0]);

  if ((DesiredFreq < 300000000) || (DesiredFreq > 1000000000)) {
    fprintf(stderr,"Frequency %d not in range 300000000 Hz - 1000000000 Hz\n",DesiredFreq);
    return 0;
  }

  ActualFreq = cc1000ComputeFreq(DesiredFreq);

  return 0;

}

