2

I am trying to convert a C module that parses the output from the Linux library rtl_fm. It is used for catching energy usage from an Efergy meter throuh a DVB-T dongle

The C module works fine but I want it written in python to interact with other python modules i have

I have put the constants in constant.py

I am totally stuck in converting the row: cursamp = (int16_t) (fgetc(stdin) | fgetc(stdin)<<8); that I have tried to convert in a lot of different ways. Every try ends with an error!

It seems to be two types of problems: 1. Type conversion of the input result 2. How to convert fgetc() into python.

I also have troubles converting while(!feof(stdin)) into python

Anyone that could help?

C code below:

#include <stdio.h>
#include <stdint.h>
#include <time.h>
#include <math.h>

#include <stdlib.h> // For exit function

#define VOLTAGE         240 /* Refernce Voltage */
#define CENTERSAMP      100 /* Number of samples needed to compute for the wave center */
#define PREAMBLE_COUNT      40  /* Number of high(1) samples for a valid preamble */
#define MINLOWBIT       3   /* Number of high(1) samples for a logic 0 */
#define MINHIGHBIT      8   /* Number of high(1) samples for a logic 1 */
#define E2BYTECOUNT     8   /* Efergy E2 Message Byte Count */
#define FRAMEBITCOUNT       64  /* Number of bits for the entire frame (not including preamble) */


#define LOGTYPE        1 // Allows changing line-endings - 0 is for Unix /n, 1 for Windows /r/n
#define SAMPLES_TO_FLUSH  10 // Number of samples taken before writing to file.
                 // Setting this too low will cause excessive wear to flash due to updates to
                 // filesystem! You have been warned! Set to 10 samples for 6 seconds = every min.

int loggingok;   // Global var indicating logging on or off
int samplecount; // Global var counter for samples taken since last flush
FILE *fp;    // Global var file handle

int calculate_watts(char bytes[])
{

char tbyte;
double current_adc;
double result;
int i;

time_t ltime; 
struct tm *curtime;
char buffer[80];

    /* add all captured bytes and mask lower 8 bits */

    tbyte = 0;

    for(i=0;i<7;i++)
        tbyte += bytes[i];

    tbyte &= 0xff;

    /* if checksum matches get watt data */

    if (tbyte == bytes[7])
    {
        time( &ltime );
        curtime = localtime( &ltime );
        strftime(buffer,80,"%x,%X", curtime);

        current_adc = (bytes[4] * 256) + bytes[5];
        result  = (VOLTAGE * current_adc) / ((double) 32768 / (double) pow(2,bytes[6]));
        printf("%s,%f\n",buffer,result);
        if(loggingok) {
          if(LOGTYPE) {
            fprintf(fp,"%s,%f\r\n",buffer,result);
          } else {
            fprintf(fp,"%s,%f\n",buffer,result);
          }
          samplecount++;
          if(samplecount==SAMPLES_TO_FLUSH) {
            samplecount=0;
            fflush(fp);
          }
        }
        fflush(stdout);
        return 1;
    }
    //printf("Checksum Error \n");
    return 0;
}

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

char bytearray[9];
char bytedata;

int prvsamp;
int hctr;
int cursamp;
int bitpos;
int bytecount;

int i;
int preamble;
int frame;
int dcenter;
int dbit;

long center;

    if(argc==2) {
      fp = fopen(argv[1], "a"); // Log file opened in append mode to avoid destroying data
      samplecount=0; // Reset sample counter
      loggingok=1;
      if (fp == NULL) {
          perror("Failed to open log file!"); // Exit if file open fails
          exit(EXIT_FAILURE);
      }
    } else {
      loggingok=0;
    }

    printf("Efergy E2 Classic decode \n\n");


    /* initialize variables */

    cursamp = 0;
    prvsamp = 0;

    bytedata = 0;
    bytecount = 0;
    hctr = 0;
    bitpos = 0;
    dbit = 0;
    preamble = 0;
    frame = 0;

    dcenter = CENTERSAMP;
    center = 0;

    while( !feof(stdin) ) 
    {

        cursamp  = (int16_t) (fgetc(stdin) | fgetc(stdin)<<8);

        /* initially capture CENTERSAMP samples for wave center computation */

        if (dcenter > 0)
        {
            dcenter--;
            center = center + cursamp;  /* Accumulate FSK wave data */ 

            if (dcenter == 0)
            {
                /* compute for wave center and re-initialize frame variables */

                center = (long) (center/CENTERSAMP);

                hctr  = 0;
                bytedata = 0;
                bytecount = 0;
                bitpos = 0;
                dbit = 0;
                preamble = 0;
                frame = 0;
            }

        }
        else
        {
            if ((cursamp > center) && (prvsamp < center))       /* Detect for positive edge of frame data */
                hctr = 0;
            else 
                if ((cursamp > center) && (prvsamp > center))       /* count samples at high logic */
                {
                    hctr++;
                    if (hctr > PREAMBLE_COUNT)  
                        preamble = 1;
                }
                else 
                    if (( cursamp < center) && (prvsamp > center))
                    {
                        /* at negative edge */

                        if ((hctr > MINLOWBIT) && (frame == 1))
                        {
                            dbit++;
                            bitpos++;   
                            bytedata = bytedata << 1;
                            if (hctr > MINHIGHBIT)
                                bytedata = bytedata | 0x1;

                            if (bitpos > 7)
                            {
                                bytearray[bytecount] = bytedata;
                                bytedata = 0;
                                bitpos = 0;

                                bytecount++;

                                if (bytecount == E2BYTECOUNT)
                                {

                                    /* at this point check for checksum and calculate watt data */
                                    /* if there is a checksum mismatch compute for a new wave center */

                                    if (calculate_watts(bytearray) == 0)
                                        dcenter = CENTERSAMP;   /* make dcenter non-zero to trigger center resampling */
                                }
                            }

                            if (dbit > FRAMEBITCOUNT)
                            {   
                                /* reset frame variables */

                                bitpos = 0;
                                bytecount = 0;
                                dbit = 0;
                                frame = 0;
                                preamble = 0;
                                bytedata = 0;
                            }
                        }

                        hctr = 0;

                    } 
                    else
                        hctr = 0;

            if ((hctr == 0) && (preamble == 1))
            {
                /* end of preamble, start of frame data */
                preamble = 0;
                frame = 1;
            }

        } /* dcenter */

        prvsamp = cursamp;

    } /* while */
    if(loggingok) {
        fclose(fp); // If rtl-fm gives EOF and program terminates, close file gracefully.
    }
}

And the Python conversion (a little simplified without file logging):

from datetime import date
from datetime import time
from datetime import datetime
import cmath
import constant
import sys
import logging

logging.basicConfig(level=logging.DEBUG)
logger = logging.getLogger(__name__)


class _Getch:
    """Gets a single character from standard input.  Does not echo to the
screen."""
    def __init__(self):
        try:
            self.impl = _GetchWindows()
        except ImportError:
            self.impl = _GetchUnix()

    def __call__(self): return self.impl()


class _GetchUnix:
    def __init__(self):
        import tty, sys

    def __call__(self):
        import sys, tty, termios
        fd = sys.stdin.fileno()
        old_settings = termios.tcgetattr(fd)
        try:
            tty.setraw(sys.stdin.fileno())
            ch = sys.stdin.read(1)
        finally:
            termios.tcsetattr(fd, termios.TCSADRAIN, old_settings)
        return ch


class _GetchWindows:
    def __init__(self):
        import msvcrt

    def __call__(self):
        import msvcrt
        return msvcrt.getch()


getch = _Getch()

def calculate_watts(*args):
    logger.info('Start Calculation')
    now = datetime.now()

    tbyte = 0

    for i in range(0,7):
        tbyte += bytes[i]

    tbyte = tbyte & 0xff

    if (tbyte == bytes[7]):
        current_adc = (bytes[4] * 256) + bytes[5]
        result = (constant.VOLTAGE * current_adc) / (32768 / pow(2,bytes[6]))
        print "%s,%f\n" % (now,result)
        exit(0)
    else:
        print "Checksum Error \n"
        exit(1)

def  main(*argv):
    logger.info('Starting Main')

    print "Efergy E2 Python decode \n\n"

    cursamp = 0
    prvsamp = 0

    bytedata = 0
    bytecount = 0
    hctr = 0
    bitpos = 0
    dbit = 0
    preamble = 0
    frame = 0

    dcenter = constant.CENTERSAMP
    center = 0

    while (1):

        cursamp  = (int)((int)(_Getch()) | (int)(_Getch())<<8)
        logger.debug('cursamp: %f',cursamp)

        if (dcenter > 0):
            dcenter -= 1
            center = center + cursamp    #/* Accumulate FSK wave data */ 
            if (dcenter == 0):

                center = (center/constant.CENTERSAMP)
                hctr  = 0
                bytedata = 0
                bytecount = 0
                bitpos = 0
                dbit = 0
                preamble = 0
                frame = 0
        else:
            if ((cursamp > center) and (prvsamp < center)):        #/* Detect for positive edge of frame data */
                hctr = 0
            else: 
                if ((cursamp > center) and (prvsamp > center)):        #/* count samples at high logic */
                    hctr += 1
                    if (hctr > constant.PREAMBLE_COUNT):    
                        preamble = 1
                else: 
                    if (( cursamp < center) and (prvsamp > center)):
                        #/* at negative edge */
                        if ((hctr > constant.MINLOWBIT) and (frame == 1)):
                            dbit += 1
                            bitpos += 1    
                            bytedata = bytedata << 1
                            if (hctr > constant.MINHIGHBIT):
                                bytedata = bytedata | 0x1
                            if (bitpos > 7):
                                bytearray[bytecount] = bytedata
                                bytedata = 0
                                bitpos = 0

                                bytecount += 1

                                if (bytecount == constant.E2BYTECOUNT):

                                    # /* at this point check for checksum and calculate watt data */
                                    #  /* if there is a checksum mismatch compute for a new wave center */

                                    if (calculate_watts(bytearray) == 0):
                                        dcenter = constant.CENTERSAMP   #/* make dcenter non-zero to trigger center resampling */

                            if (dbit > constant.FRAMEBITCOUNT):
                                #/* reset frame variables */

                                bitpos = 0
                                bytecount = 0
                                dbit = 0
                                frame = 0
                                preamble = 0
                                bytedata = 0

                        hctr = 0

                    else:
                        hctr = 0

            if ((hctr == 0) and (preamble == 1)):

                #/* end of preamble, start of frame data */
                preamble = 0
                frame = 1

         #/* dcenter */

        prvsamp = cursamp

if __name__ == "__main__":
  main()
0

1 Answer 1

5

C's fgetc(stdin) translates to Python 2's ord(sys.stdin.read(1)[0]) -- returning a numeric value from the next byte of stdin. (In Python 3, you'll have to reopen sys.stdin as binary to achieve that, otherwise .read(1) will get a Unicode character, not a byte).

The | and << operators work the same in Python as in C, so, no problem there.

At EOF, sys.stdin.read(1) returns an empty list (so the [0] would fail, but you can check for that by "decomposing" the above expression). For example:

ateof = False

def getabyte():
    data = sys.stdin.read(1)
    if data: return False, ord(data)
    else: return True, 0

def getanint():
    global ateof
    ateof, byte1 = getabyte()
    if not ateof:
        ateof, byte2 = getabyte()
    if ateof: return True, 0
    else: return False, byte1 | (byte2<<8)

net of finicky issues wrt endianness (byte order) and character's signedness (issues common to C and Python).

Sign up to request clarification or add additional context in comments.

3 Comments

Thank's @Alex, that seems to work fine! However, I got stuck in another part ´ tbyte = 0 # for(i=0;i<7;i++) # tbyte += bytes[i]; for i in range(0,7): tbyte += bytes[i]´ that render the error: TypeError: 'type' object has no attribute 'getitem'
@MikaelLjunggren, happy to help, but please accept the answer that helps, don't just thank for it!-)
@MikaelLjunggren that's fine, we've all been newbies at some time:-), but clicking on the checkmark you see to the left of the answer is how you accept an answer, and it's crucial to SO's functioning that the helpful answer be accepted.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.