#!/usr/bin/env python
#
# Copyright 2004 Free Software Foundation, Inc.
#
# This file is part of GNU Radio
#
# GNU Radio is free software; you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation; either version 2, or (at your option)
# any later version.
#
# GNU Radio 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 GNU Radio; see the file COPYING.  If not, write to
# the Free Software Foundation, Inc., 59 Temple Place - Suite 330,
# Boston, MA 02111-1307, USA.
#


import re
import math
import sys
import operator




######################################################################
# Decimal to any base conversion.
# Convert 'num' to a list of 'l' numbers representing 'num'
# to base 'base' (most significant symbol first).
######################################################################
def dec2base(num,base,l):
    s=range(l)
    n=num
    for i in range(l):
        s[l-i-1]=n%base
        n=int(n/base)
    if n!=0:
        print 'Number ', num, ' requires more than ', l, 'digits.'
    return s


######################################################################
# Conversion from any base to decimal.
# Convert a list 's' of symbols to a decimal number
# (most significant symbol first)
######################################################################
def base2dec(s,base):
    num=0
    for i in range(len(s)):
        num=num*base+s[i]
    return num







######################################################################
# Automaticaly generate the FSM structure for a binary feed-forward
# convolutional code.
# Input: k x n generator matrix (decimal representation)
######################################################################
def make_fsm_bin_cc_ff(k,n,GM):
    mem=[[]]*k
    max_mem_x=[-1]*k
    max_mem = -1
    for i in range(k):
        memr=[0]*n
        for j in range(n):
            if GM[i][j]==0:
                memr[j]=-1
            else:
                memr[j]=int(math.log(GM[i][j],2))
            if memr[j]>max_mem_x[i]:
                max_mem_x[i]=memr[j]
            if memr[j]>max_mem:
                max_mem=memr[j]
        mem[i]=memr

    sum_max_mem = 0
    for i in range(k):
       sum_max_mem = sum_max_mem+max_mem_x[i] 
        

    #print mem
    #print max_mem_x
    #print max_mem
    #print sum_max_mem

    I=2**k
    S=2**sum_max_mem
    O=2**n

    #print I, S, O

    NS=[0]*S*I;
    OS=[0]*S*I;
    for s in range(S):
        for i in range(I):
            ss=dec2base(s,2,sum_max_mem)
            ind=0
            ss_r=[]
            for kk in range(k):
                ss1 = [0]*max_mem
                ss1[0:max_mem_x[kk]] = ss[ind:ind+max_mem_x[kk]]
                ss_r.append(ss1)
                ind=ind+max_mem_x[kk]
            ii=dec2base(i,2,k)

            tt_r = ss_r
            for kk in range(k):
                tt_r[kk].insert(0,ii[kk])
            #print tt_r

            ns_r = []
            for kk in range(k):
                ns_r.append(tt_r[kk][0:max_mem])

            ns=[]
            for kk in range(k):
                ns = ns + ns_r[kk][0:max_mem_x[kk]]
            NS[s*I+i]=base2dec(ns,2);

            out_r=[0]*n
            for nn in range(n):
                out=0;
                for kk in range(k):
                    c=[0]*max_mem
                    gm = dec2base(GM[kk][nn],2,max_mem_x[kk]+1)
                    gm.reverse()
                    c[0:len(gm)] = gm
                    sy = 0
                    for m in range(len(c)):
                        sy = sy + c[m]*tt_r[kk][m];
                    out=operator.mod(out+sy,2);
                out_r[nn]=out;
            out_r.reverse()
            OS[s*I+i] = base2dec(out_r,2);
    
    #O=max(max(OS))+1;
    print I, S, O
    print NS
    print OS

    return (I,S,O,NS,OS)





######################################################################
# Automatically generate the lookup table that maps the FSM outputs
# to channel inputs corresponding to a channel 'channel' and a modulation
# 'mod'. Optional normalization of channel to unit energy.
# This table is used by the 'metrics' block to translate
# channel outputs to metrics for use with the Viterbi algorithm. 
# Limitations: currently supports only one-dimensional modulations.
######################################################################
def make_isi_lookup(mod,channel,normalize):
    dim=mod[0]
    constellation = mod[1]

    if normalize:
        p = 0
        for i in range(len(channel)):
            p = p + channel[i]**2
        for i in range(len(channel)):
            channel[i] = channel[i]/math.sqrt(p)

    lookup=range(len(constellation)**len(channel))
    for o in range(len(constellation)**len(channel)):
        ss=dec2base(o,len(constellation),len(channel))
        ll=0
        for i in range(len(channel)):
            ll=ll+constellation[ss[i]]*channel[i]
        lookup[o]=ll
    return (1,lookup)


    



######################################################################
# A list of common modulations.
# Format: (dimensionality,constellation)
######################################################################
pam2 = (1,[-1, 1])
pam4 = (1,[-3, -1, 3, 1])		# includes Gray mapping
pam8 = (1,[-7, -5, -3, -1, 1, 3, 5, 7])

psk4=(2,[1, 0, \
         0, 1, \
         0, -1,\
        -1, 0])				# includes Gray mapping
psk8=(2,[math.cos(2*math.pi*0/8), math.sin(2*math.pi*0/8),  \
         math.cos(2*math.pi*1/8), math.sin(2*math.pi*1/8),  \
         math.cos(2*math.pi*2/8), math.sin(2*math.pi*2/8),  \
         math.cos(2*math.pi*3/8), math.sin(2*math.pi*3/8),  \
         math.cos(2*math.pi*4/8), math.sin(2*math.pi*4/8),  \
         math.cos(2*math.pi*5/8), math.sin(2*math.pi*5/8),  \
         math.cos(2*math.pi*6/8), math.sin(2*math.pi*6/8),  \
         math.cos(2*math.pi*7/8), math.sin(2*math.pi*7/8)])

orth2 = (2,[1, 0, \
            0, 1])
orth4=(4,[1, 0, 0, 0, \
          0, 1, 0, 0, \
          0, 0, 1, 0, \
          0, 0, 0, 1])

######################################################################
# A list of channels to be tested
######################################################################

# C test channel (J. Proakis, Digital Communications, McGraw-Hill Inc., 2001)
c_channel = [0.227, 0.460, 0.688, 0.460, 0.227]










if __name__ == '__main__':
    make_fsm_bin_cc_ff (1,2,[[7,5]])
    print "----------\n"
    make_fsm_bin_cc_ff (2,3,[[1,0,2],[0,1,6]])