Difference in Precision Between Python Function and C Function - python

I'm working with some code that does a mathematical conversion from Earth Centered Earth Fixed Coordinates to Latitude/Longitude/Altitude Coordinates. One iteration of this function is written in Python- another in C. The C method came after the Python method was written and is supposed to be a direct translation. However, I'm finding that the Python method, although it's using floating point values for it's calculations, gets a correct answer, while the C method, even when using doubles, seems to only be rounding after only 5 decimal places and therefore gets an incorrect answer when attempting to convert ECEF to LLA coordinates. Here is the Python method for the conversion, along with an example coordinate:
import math
import numpy as np
def ecef2lla(eceflist):
x = float(eceflist[0])
y = float(eceflist[1])
z = float(eceflist[2])
a = 6378137 # radius
e = 8.1819190842622e-2 # eccentricity
e_string = "E value:" + " " + str(e)
print(e_string)
asq = math.pow(a,2)
asq_string = "asq value:" + " " + str(asq)
print(asq_string)
esq=math.pow(e,2)
esq_string = "esq value:" + " " + str(esq)
print(esq_string)
b = math.sqrt( asq * (1-esq) )
b_string = "b value:" + " " + str(b)
print(b_string)
bsq = math.pow(b,2)
ep = math.sqrt( (asq - bsq)/bsq)
ep_string = "ep value:" + " " + str(ep)
print(ep_string)
p = math.sqrt( math.pow(x,2) + math.pow(y,2) )
p_string = "p value:" + " " + str(p)
print(p_string)
th = math.atan2(a*z, b*p)
th_string = "th value:" + " " + str(th)
print(th_string)
lon = math.atan2(y,x)
lon_string = "lon value:" + " " + str(lon)
print(lon_string)
lat = math.atan2( (z + math.pow(ep,2)*b*math.pow(math.sin(th),3) ), (p - esq*a*math.pow(math.cos(th),3)))
lat_string = "lat value:" + " " + str(lat)
print(lat_string)
N = a/( math.sqrt(1-esq*math.pow(math.sin(lat),2)) )
N_divisor = math.sqrt(1-esq*math.pow(math.sin(lat),2))
ND_string = "N divisor value is" + " " + str(N_divisor)
print(ND_string)
N_string = "N value:" + " " + str(N)
print(N_string)
alt = p / math.cos(lat) - N
alt_string = "alt value:" + " " + str(alt)
print(alt_string)
lon = lon % (2*math.pi)
ret = [lat*180/math.pi, lon*180/math.pi, alt]
return ret
if __name__ == '__main__':
lla_coords = ecef2lla([2155172.63, 2966340.65, 5201390])
print(lla_coords)
The following method is the translated C method, using double data types, with thinking being this would ensure maximum precision:
#include <stdlib.h>
#include <string.h>
#include <math.h>
#include <stdio.h>
#include <unistd.h>
void ecef_to_lla(double *coordinate){
double ecef_latitude = coordinate[0];
double ecef_longitude = coordinate[1];
double ecef_altitude = coordinate[2];
double a = 6378137;
//double e = 8.1819190842622e-2;
double e = 0.081819190842622;
printf("E value: %.16f\n",e);
double asq = pow(a,2);
printf("asq string: %.16f\n",asq);
double esq = pow(e,2);
printf("esq string: %.16f\n",esq);
double b = sqrt(asq * (1-esq));
printf("b string: %.16f\n",b);
double bsq = pow(b,2);
double ep = sqrt((asq-bsq)/bsq);
printf("ep string: %.16f\n",ep);
double p = sqrt(pow(ecef_latitude,2) + pow(ecef_longitude,2));
printf("p string: %.16f\n",p);
double th = atan2(a*ecef_altitude,b*p);
printf("th string: %.16f\n",th);
double lla_longitude = atan2(ecef_longitude,ecef_latitude);
double lla_latitude = atan2((ecef_altitude + pow(ep,2)*b*pow(sin(th),2)),p-esq*a*pow(cos(th),3));
printf("Lat is %.16f\n",lla_latitude);
double n = a/(sqrt(1-esq*pow(sin(lla_latitude),2)));
double n_divisor = sqrt(1-esq*pow(sin(lla_latitude),2));
printf("n_divisor is %.16f\n",n_divisor);
printf("n is %.16f\n",n);
double lla_altitude = p / cos(lla_latitude) - n;
printf("alt is %.16f\n",lla_altitude);
lla_longitude = fmod(lla_longitude ,(2 * M_PI));
lla_latitude = fmod(lla_latitude ,(2 * M_PI));
lla_latitude = lla_latitude * 180/M_PI;
lla_longitude = lla_longitude * 180/M_PI;
coordinate[0] = lla_latitude;
coordinate[1] = lla_longitude;
coordinate[2] = lla_altitude;
}
int main(void){
double coordinates[3] = {2155172.63, 2966340.65, 5201390};
ecef_to_lla(coordinates);
}
The LLA coordinates returned by the Python method are:
54.99999538240099, 54.00000006037918, 8.26665045041591
while those returned by the C method are:
55.0268382213345930,54.0000000603791790,4279.4874338442459702
So the altitude calculationseems to be the issue

There's a tiny transcription error. Change the line
double lla_latitude = atan2((ecef_altitude + pow(ep,2)*b*pow(sin(th),2)),p-esq*a*pow(cos(th),3));
to
double lla_latitude = atan2((ecef_altitude + pow(ep,2)*b*pow(sin(th),3)),p-esq*a*pow(cos(th),3));
When I make that change, I get 8.2666504495 for the altitude.

Related

Error in Gathering Statistics on the network traffic

I'm creating a function to calculate packet capture delay using Python and pcap, but this error happens. I'm taking this function from a C example as I'm new to Python. This is the error:
Traceback (most recent call last):
File "prototype_sniffer_v1.py", line 266, in processing_pkts
Bps = ((pkt + 8) * 8 * 1000000) / (delay) TypeError: unsupported operand type(s) for +: 'LP_c_ubyte' and 'int'
Exception ignored on calling ctypes callback function: <function processing_pkts at 0x7fbed299df70>
This link Gathering Statistics on the network traffic contain the C code example.
This is C example:
#include <stdlib.h>
#include <stdio.h>
#include <pcap.h>
#include <tchar.h>
BOOL LoadNpcapDlls()
{
_TCHAR npcap_dir[512];
UINT len;
len = GetSystemDirectory(npcap_dir, 480);
if (!len) {
fprintf(stderr, "Error in GetSystemDirectory: %x", GetLastError());
return FALSE;
}
_tcscat_s(npcap_dir, 512, _T("\\Npcap"));
if (SetDllDirectory(npcap_dir) == 0) {
fprintf(stderr, "Error in SetDllDirectory: %x", GetLastError());
return FALSE;
}
return TRUE;
}
void usage();
void dispatcher_handler(u_char *, const struct pcap_pkthdr *, const u_char *);
void main(int argc, char **argv)
{
pcap_t *fp;
char errbuf[PCAP_ERRBUF_SIZE];
struct timeval st_ts;
u_int netmask;
struct bpf_program fcode;
/* Load Npcap and its functions. */
if (!LoadNpcapDlls())
{
fprintf(stderr, "Couldn't load Npcap\n");
exit(1);
}
/* Check the validity of the command line */
if (argc != 2)
{
usage();
return;
}
/* Open the output adapter */
if ( (fp= pcap_open(argv[1], 100, PCAP_OPENFLAG_PROMISCUOUS,
1000, NULL, errbuf) ) == NULL)
{
fprintf(stderr,"\nUnable to open adapter %s.\n", errbuf);
return;
}
/* Don't care about netmask, it won't be used for this filter */
netmask=0xffffff;
//compile the filter
if (pcap_compile(fp, &fcode, "tcp", 1, netmask) <0 )
{
fprintf(stderr,"\nUnable to compile the packet filter. Check the syntax.\n");
/* Free the device list */
return;
}
//set the filter
if (pcap_setfilter(fp, &fcode)<0)
{
fprintf(stderr,"\nError setting the filter.\n");
pcap_close(fp);
/* Free the device list */
return;
}
/* Put the interface in statstics mode */
if (pcap_setmode(fp, MODE_STAT)<0)
{
fprintf(stderr,"\nError setting the mode.\n");
pcap_close(fp);
/* Free the device list */
return;
}
printf("TCP traffic summary:\n");
/* Start the main loop */
pcap_loop(fp, 0, dispatcher_handler, (PUCHAR)&st_ts);
pcap_close(fp);
return;
}
void dispatcher_handler(u_char *state,
const struct pcap_pkthdr *header,
const u_char *pkt_data)
{
struct timeval *old_ts = (struct timeval *)state;
u_int delay;
LARGE_INTEGER Bps,Pps;
struct tm ltime;
char timestr[16];
time_t local_tv_sec;
/* Calculate the delay in microseconds from the last sample. This value
* is obtained from the timestamp that the associated with the sample. */
delay = (header->ts.tv_sec - old_ts->tv_sec) * 1000000
- old_ts->tv_usec + header->ts.tv_usec;
/* Get the number of Bits per second */
Bps.QuadPart=(((*(LONGLONG*)(pkt_data + 8)) * 8 * 1000000) / (delay));
/* ^ ^
| |
| |
| |
converts bytes in bits -- |
|
delay is expressed in microseconds --
*/
/* Get the number of Packets per second */
Pps.QuadPart=(((*(LONGLONG*)(pkt_data)) * 1000000) / (delay));
/* Convert the timestamp to readable format */
local_tv_sec = header->ts.tv_sec;
localtime_s(&ltime, &local_tv_sec);
strftime( timestr, sizeof timestr, "%H:%M:%S", &ltime);
/* Print timestamp*/
printf("%s ", timestr);
/* Print the samples */
printf("BPS=%I64u ", Bps.QuadPart);
printf("PPS=%I64u\n", Pps.QuadPart);
//store current timestamp
old_ts->tv_sec=header->ts.tv_sec;
old_ts->tv_usec=header->ts.tv_usec;
}
void usage()
{
printf("\nShows the TCP traffic load, in bits per second and packets per second."
"\nCopyright (C) 2002 Loris Degioanni.\n");
printf("\nUsage:\n");
printf("\t tcptop adapter\n");
printf("\t You can use \"WinDump -D\" if you don't know the name of your adapters.\n");
exit(0);
}
This is my Python function:
import libpcap as pcap
import ctypes as ct
import socket
import getopt
import os
import time
import funcoes_auxiliares
from struct import *
from libpcap._platform import sockaddr, sockaddr_in, sockaddr_in6
from pcaptestutils import *
pd = ct.POINTER(pcap.pcap_t)()
if not is_windows:
breaksigint = False
#static void sigint_handler(int signum _U_)
def sigint_handler(signum):
global pd
global breaksigint
if breaksigint:
pcap.breakloop(pd)
def main(argv=sys.argv[1:]):
global program_name
program_name = os.path.basename(sys.argv[0])
global pd
global breaksigint
global ip_afinet
global ip_afinet6
# Exceção para os parâmetros
try:
opts, args = getopt.getopt(argv, "i:mnt:" if is_windows else "bi:mnrst:")
except getopt.GetoptError:
usage()
device = None
timeout = 1000
nonblock = 0
immediate = False
if not is_windows:
sigrestart = False
catchsigint = False
for opt, optarg in opts:
if not is_windows and opt == '-b':
breaksigint = True
elif opt == '-i':
device = optarg.encode("utf-8")
elif opt == '-m':
immediate = True
elif opt == '-n':
nonblock = 1
elif not is_windows and opt == '-r':
sigrestart = True
elif not is_windows and opt == '-s':
catchsigint = True
elif opt == '-t':
try:
timeout = int(optarg)
except:
error('Timeout value "{}" is not a number', optarg)
if timeout < 0:
error("Timeout value {:d} is negative", timeout)
if timeout > INT_MAX:
error("Timeout value {:d} is too large (> {:d})",
timeout, INT_MAX)
else:
usage()
expression = args
errbuf = ct.create_string_buffer(pcap.PCAP_ERRBUF_SIZE)
# ----------------------------------------------------------
# Lista de dispositivos disponíveis
if device is None:
deviceList = ct.POINTER(pcap.pcap_if_t)()
if pcap.findalldevs(ct.byref(deviceList), errbuf) == -1:
error("{}", ebuf2str(errbuf))
if not deviceList:
error("Não há interfaces disponíveis para captura")
print('\nAvailable network devices:')
devices = deviceList
while devices:
device = devices.contents
print("\t[*] {}".format(device.name.decode("utf-8")))
devices = device.next
device = deviceList[0].name
ip_tuple = funcoes_auxiliares.getStdIp(deviceList[0])
(ip_afinet, ip_afinet6) = ip_tuple
# print(ip_afinet)
# print(ip_afinet6)
pcap.freealldevs(deviceList)
# ----------------------------------------------------------
errbuf[0] = b"\0"
# if not is_windows:
# # If we were told to catch SIGINT, do so.
# if catchsigint:
# action = sigaction()
# action.sa_handler = sigint_handler
# sigemptyset(ct.byref(action.sa_mask))
# # Should SIGINT interrupt, or restart, system calls?
# action.sa_flags = SA_RESTART if sigrestart else 0
# if sigaction(SIGINT, ct.byref(action), NULL) == -1:
# error("Can't catch SIGINT: {}", strerror(errno))
pd = pcap.create(device, errbuf)
if not pd:
error("{}", ebuf2str(errbuf))
# define o comprimento do instantâneo a ser usado
# em um identificador de captura quando o
# identificador é ativado para snaplen.
status = pcap.set_snaplen(pd, 65535)
if status != 0:
error("{}: pcap.set_snaplen failed: {}",
device2str(device), status2str(status));
if immediate:
try:
status = pcap.set_immediate_mode(pd, 1)
except AttributeError:
error("pcap.set_immediate_mode is not available on this platform")
if status != 0:
error("{}: pcap.set_immediate_mode failed: {}",
device2str(device), status2str(status))
status = pcap.set_timeout(pd, timeout)
if status != 0:
error("{}: pcap.set_timeout failed: {}",
device2str(device), status2str(status))
status = pcap.activate(pd)
if status < 0:
# pcap.activate() failed.
error("{}: {}\n({})",
device2str(device), status2str(status), geterr2str(pd))
elif status > 0:
# pcap.activate() succeeded, but it's warning us
# of a problem it had.
warning("{}: {}\n({})",
device2str(device), status2str(status), geterr2str(pd))
localnet = pcap.bpf_u_int32()
netmask = pcap.bpf_u_int32()
if pcap.lookupnet(device, ct.byref(localnet), ct.byref(netmask), errbuf) < 0:
localnet = pcap.bpf_u_int32(0)
netmask = pcap.bpf_u_int32(0)
warning("{}", ebuf2str(errbuf))
fcode = pcap.bpf_program()
cmdbuf = " ".join(expression).encode("utf-8")
if pcap.compile(pd, ct.byref(fcode), cmdbuf, 1, netmask) < 0:
error("{}", geterr2str(pd))
if pcap.setfilter(pd, ct.byref(fcode)) < 0:
error("{}", geterr2str(pd))
if pcap.setnonblock(pd, nonblock, errbuf) == -1:
error("pcap.setnonblock failed: {}", ebuf2str(errbuf))
# -----------------------------------------
# create a save file for write
savefile = 'testsavefile'
savefile = savefile.encode('utf-8')
pdd = pcap.dump_open_append(pd, savefile)
if not pdd:
error("{}", geterr2str(pd))
# -----------------------------------------
print("\nSniffing on device: \n\t*{}*".format(device2str(device)))
if os.path.exists("qtdPktpTime.txt"):
os.remove("qtdPktpTime.txt")
cont_i = 0
while cont_i <= 10:
# while True:
packet_count = ct.c_int(0)
status = pcap.dispatch(pd, -1, processing_pkts,
ct.cast(ct.pointer(packet_count), ct.POINTER(ct.c_ubyte)))
# execute for write in save file
pcap.dispatch(pd, -1, pcap.dump,
ct.cast(pdd, ct.POINTER(ct.c_ubyte)))
if status < 0:
break
if status != 0:
print("\n{:d} packets seen, {:d} packets counted after "
"pcap.dispatch returns".format(status, packet_count.value))
ps = pcap.stat()
pcap.stats(pd, ct.byref(ps))
print("{:d} ps_recv, {:d} ps_drop, {:d} ps_ifdrop".format(
ps.ps_recv, ps.ps_drop, ps.ps_ifdrop))
print("\n")
cont_i += 1
if status == pcap.PCAP_ERROR_BREAK:
# We got interrupted, so perhaps we didn't manage to finish a
# line we were printing. Print an extra newline, just in case.
print()
print("Broken out of loop from SIGINT handler")
sys.stdout.flush()
if status == pcap.PCAP_ERROR:
# Error. Report it.
print("{}: pcap.dispatch: {}".format(program_name, geterr2str(pd)),
file=sys.stderr)
pcap.freecode(ct.byref(fcode))
pcap.close(pd)
funcoes_auxiliares.plotGraf1()
return 1 if status == -1 else 0
# --------------------------------------------------------------------------------------
# AUXILIAR FUNCTIONS
# --------------------------------------------------------------------------------------
# Convert a string of 6 characters of ethernet address into a dash separated hex string
def eth_addr (a) :
b = "%.2x:%.2x:%.2x:%.2x:%.2x:%.2x" % (ord(a[0]) , ord(a[1]) , ord(a[2]), ord(a[3]), ord(a[4]) , ord(a[5]))
return b
# Função callback usada no processamento dos pacotes
#pcap.pcap_handler
def processing_pkts(arg, hdr, pkt):
counterp = ct.cast(arg, ct.POINTER(ct.c_int))
counterp[0] += 1
old_ts_tv_sec = 0
old_ts_tv_usec = 0
pkt_addr = hdr.contents
pkt_time_cap = pkt_addr.ts
pkt_len = pkt_addr.len
pkt_caplen = pkt_addr.caplen
eth_length = 14
eth_header = pkt[:eth_length]
eth = unpack('!6s6sH' , bytes(eth_header))
eth_protocol = socket.ntohs(eth[2])
count_pkt = 0
delay = (pkt_time_cap.tv_sec - old_ts_tv_sec) * 1000000 - old_ts_tv_usec + pkt_time_cap.tv_usec
Bps = (ct.cast(pkt,ct.POINTER(ct.c_uint64))[1] * 8 * 1000000) / (delay)
Pps = (ct.cast(pkt,ct.POINTER(ct.c_uint64))[0] * 1000000) / delay
old_ts_tv_sec = pkt_time_cap.tv_sec
old_ts_tv_usec = pkt_time_cap.tv_usec
print('Bps: ' + str(int(Bps)))
print('Pps: ' + str(int(Pps)))
time_cap = time.ctime(pkt_time_cap.tv_sec)
# Parser IP packets
if eth_protocol == 8:
ip_header = pkt[eth_length:20+eth_length]
iph = unpack('!BBHHHBBH4s4s' , bytes(ip_header))
version_ihl = iph[0]
version = version_ihl >> 4
ihl = version_ihl & 0xF
iph_length = ihl * 4
ttl = iph[5]
protocol = iph[6]
s_addr = socket.inet_ntoa(iph[8]);
d_addr = socket.inet_ntoa(iph[9]);
# print('Version: ' + str(version) + '| IP Header Length: ' + str(ihl) + ' | ' + 'TTL: ' + str(ttl) + ' | ' + 'Protocol: ' + str(protocol) + ' | ' +'Source Address: ' + str(s_addr) + ' | ' +'Destination Address: ' + str(d_addr) + ' | ' + 'length: ' + str(pkt_len) + ' | ' + 'cap length: ' + str(pkt_caplen) + ' | ' + 'Cap time: ' + str(pkt_time_cap))
print('\nVersion: ' + str(version) + '| IP Header Length: ' + str(ihl) + ' | ' + 'TTL: ' + str(ttl) + ' | ' + 'Protocol: ' + str(protocol) + ' | ' +'Source Address: ' + str(s_addr) + ' | ' +'Destination Address: ' + str(d_addr) + ' | ' + 'length: ' + str(pkt_len))
count_pkt = 0
if s_addr == ip_afinet:
# print('confirma ip')
count_pkt += 1
arq_qtdPktpTime = open("qtdPktpTime.txt", "a")
arq_qtdPktpTime.write(time_cap + ',' + str(counterp[0]) + '\n')
# funcoes_auxiliares.plotGraf1()
# TCP protocol
if protocol == 6:
t = iph_length + eth_length
tcp_header = pkt[t:t+20]
tcph = unpack('!HHLLBBHHH' , bytes(tcp_header))
source_port = tcph[0]
dest_port = tcph[1]
sequence = tcph[2]
acknowledgement = tcph[3]
doff_reserved = tcph[4]
tcph_length = doff_reserved >> 4
print('Source Port: ' + str(source_port) + ' >> Dest Port: ' + str(dest_port) + ' >> Sequence Number: ' + str(sequence) + ' >> Acknowledgement: ' + str(acknowledgement) + ' >> TCP header length: ' + str(tcph_length))
h_size = eth_length + iph_length + tcph_length * 4
# data_size = len(pkt) - h_size
# data_size = pkt_len - h_size
#get data from the packet
data = pkt[h_size:pkt_len]
# print('Data : ' + str(data))
# UDP procotol
elif protocol == 17:
u = iph_length + eth_length
udph_length = 8
udp_header = pkt[u:u+8]
udph = unpack('!HHHH' , bytes(udp_header))
source_port = udph[0]
dest_port = udph[1]
length = udph[2]
checksum = udph[3]
# print('Source Port: ' + str(source_port) + ' >> Dest Port: ' + str(dest_port) + '>> Length: ' + str(length) + ' >> Checksum: ' + str(checksum))
print('Source Port: ' + str(source_port) + ' >> Dest Port: ' + str(dest_port) + ' >> Checksum: ' + str(checksum))
h_size = eth_length + iph_length + udph_length
# data = pkt[h_size:pkt_len]
# print(data)
# pcap.dump
# função para captura e uso de parametros no execução do código no terminal
def usage():
print("Usage: {} [ {} ] [ -i interface ] [ -t timeout] "
"[ expression ]".format(program_name,
"-mn" if is_windows else "-bmnrs"), file=sys.stderr)
sys.exit(1)
# --------------------------------------------------------------------------------------
if __name__.rpartition(".")[-1] == "__main__":
sys.exit(main())
Can help me to solve this ?? I appreciate any tips
The C code the OP is porting and generating an error in Python is:
/* Calculate the delay in microseconds from the last sample. This value
* is obtained from the timestamp that the associated with the sample. */
delay = (header->ts.tv_sec - old_ts->tv_sec) * 1000000
- old_ts->tv_usec + header->ts.tv_usec;
/* Get the number of Bits per second */
Bps.QuadPart=(((*(LONGLONG*)(pkt_data + 8)) * 8 * 1000000) / (delay));
/* ^ ^
| |
| |
| |
converts bytes in bits -- |
|
delay is expressed in microseconds --
*/
/* Get the number of Packets per second */
Pps.QuadPart=(((*(LONGLONG*)(pkt_data)) * 1000000) / (delay));
The Python code is missing the casting and dereferencing of the pkt_data + 8 byte pointer calculation. Since *(LONGLONG*)(pkt_data + 8) == ((LONGLONG*)(pkt_data))[1], the Python code implements cast and dereference of the latter version:
delay = (pkt_time_cap.tv_sec - old_ts_tv_sec) * 1000000 - old_ts_tv_usec + pkt_time_cap.tv_usec
Bps = ct.cast(pkt,ct.POINTER(ct.c_uint64))[1] * 8 * 1000000 / delay
Pps = ct.cast(pkt,ct.POINTER(ct.c_uint64))[0] * 1000000 / delay
Since there isn't a functional example I'll demonstrate by creating a byte pointer to two 64-bit values to test the code is correct. The original code was adding 8 to a byte pointer, casting to a 64-bit pointer and dereferencing it. That's the same thing as casting directly to a 64-bit pointer and indexing the 2nd value, e.g. *(LONGLONG*)(pkt_data + 8) == ((LONGLONG*)(pkt_data))[1]:
>>> import ctypes as ct
>>> data = (ct.c_uint64 * 2)(100,200)
>>> pkt = ct.cast(ct.byref(data), ct.POINTER(ct.c_ubyte))
>>> pkt
<__main__.LP_c_ubyte object at 0x000002D38C9BA5C0>
>>> pkt + 8
Traceback (most recent call last):
File "<interactive input>", line 1, in <module>
TypeError: unsupported operand type(s) for +: 'LP_c_ubyte' and 'int'
Above demonstrates the same error as the OP due to pkt being a byte pointer. Below casts the pointer back to a 64-bit pointer and accesses the 2nd value:
>>> ct.cast(pkt,ct.POINTER(ct.c_uint64))[1]
200

Difference between masking and querying pandas.DataFrame

My example shows when using DataFrame of float that querying might in certains cases be faster than using masks. When you look at the graph, the q̶u̶e̶r̶y̶ ̶f̶u̶n̶c̶t̶i̶o̶n̶ ̶p̶e̶r̶f̶o̶r̶m̶s̶ ̶b̶e̶t̶t̶e̶r̶ ̶w̶h̶e̶n̶ ̶t̶h̶e̶ ̶c̶o̶n̶d̶i̶t̶i̶o̶n̶ ̶i̶s̶ ̶c̶o̶m̶p̶o̶s̶e̶d̶ ̶o̶f̶ ̶1̶ ̶t̶o̶ ̶5̶ ̶s̶u̶b̶c̶o̶n̶d̶i̶t̶i̶o̶n̶s̶.
Edit (thanks to a_guest): mask function performs better when the condition is composed of 1 to 5 subconditions
Then, Is there any difference between the two methods since it tends to have the same trend over the number of subconditions.
The function used to plot my data:
import matplotlib.pyplot as plt
def graph(data):
t = [int(i) for i in range(1, len(data["mask"]) + 1)]
plt.xlabel('Number of conditions')
plt.ylabel('timeit (ms)')
plt.title('Benchmark mask vs query')
plt.grid(True)
plt.plot(t, data["mask"], 'r', label="mask")
plt.plot(t, data["query"], 'b', label="query")
plt.xlim(1, len(data["mask"]))
plt.legend()
plt.show()
The functions used to creates the conditions to be tested by timeit:
def create_multiple_conditions_mask(columns, nb_conditions, condition):
mask_list = []
for i in range(nb_conditions):
mask_list.append("(df['" + columns[i] + "']" + " " + condition + ")")
return " & ".join(mask_list)
def create_multiple_conditions_query(columns, nb_conditions, condition):
mask_list = []
for i in range(nb_conditions):
mask_list.append(columns[i] + " " + condition)
return "'" + " and ".join(mask_list) + "'"
The function to benchmark masking vs querying using a pandas DataFrame containing float:
def benchmarks_mask_vs_query(dim_df=(50,10), labels=[], condition="> 0", random=False):
# init local variable
time_results = {"mask": [], "query": []}
nb_samples, nb_columns = dim_df
all_labels = list('ABCDEFGHIJKLMNOPQRSTUVWXYZ')
if nb_columns > 26:
if len(labels) == nb_columns:
all_labels = labels
else:
raise Exception("labels length must match nb_columns" )
df = pd.DataFrame(np.random.randn(nb_samples, nb_columns), columns=all_labels[:nb_columns])
for col in range(nb_columns):
if random:
condition = "<" + str(np.random.random(1)[0])
mask = "df[" + create_multiple_conditions_mask(df.columns, col+1, condition) + "]"
query = "df.query(" + create_multiple_conditions_query(df.columns, col+1, condition) + ")"
print("Parameters: nb_conditions=" + str(col+1) + ", condition= " + condition)
print("Mask created: " + mask)
print("Query created: " + query)
print()
result_mask = timeit(mask, number=100, globals=locals()) * 10
result_query = timeit(query, number=100, globals=locals()) * 10
time_results["mask"].append(result_mask)
time_results["query"].append(result_query)
return time_results
What I run:
# benchmark on a DataFrame of shape(50,25) populating with random values
# as well as the conditions ("<random_value")
data = benchmarks_mask_vs_query((50,25), random=True)
graph(data)
What I get:

Parse C-like declarations using pyparsing

I would like to parse declarations using pyparsing in a C-like source (GLSL code) such that I get a list of (type, name, value).
For example:
int a[3];
int b=1, c=2.0;
float d = f(z[2], 2) + 3*g(4,a), e;
Point f = {1,2};
I would like to obtain something like:
[ ('int', 'a[3]', ''),
('int', 'b', '1'),
('int', 'c', '2.0'),
('float', 'd', 'f(z[2], 2) + 3*g(4,a)'),
('float', 'e', ''),
('Point', 'f', '{1,2}') ]
I've played with Forward() and operatorPrecedence() to try to parse the rhs expression but I suspect it is not necessary in my case.
So far I have:
IDENTIFIER = Regex('[a-zA-Z_][a-zA-Z_0-9]*')
INTEGER = Regex('([+-]?(([1-9][0-9]*)|0+))')
EQUAL = Literal("=").suppress()
SEMI = Literal(";").suppress()
SIZE = INTEGER | IDENTIFIER
VARNAME = IDENTIFIER
TYPENAME = IDENTIFIER
VARIABLE = Group(VARNAME.setResultsName("name")
+ Optional(EQUAL + Regex("[^,;]*").setResultsName("value")))
VARIABLES = delimitedList(VARIABLE.setResultsName("variable",listAllMatches=True))
DECLARATION = (TYPENAME.setResultsName("type")
+ VARIABLES.setResultsName("variables", listAllMatches=True) + SEMI)
code = """
float a=1, b=3+f(2), c;
float d=1.0, e;
float f = z(3,4);
"""
for (token, start, end) in DECLARATION.scanString(code):
for variable in token.variable:
print token.type, variable.name, variable.value
but the last expression (f=z(3,4)) is not parsed because of the ,.
There is a C struct parser on the pyparsing wiki that might give you a good start.
This seems to work.
IDENTIFIER = Word(alphas+"_", alphas+nums+"_" )
INT_DECIMAL = Regex('([+-]?(([1-9][0-9]*)|0+))')
INT_OCTAL = Regex('(0[0-7]*)')
INT_HEXADECIMAL = Regex('(0[xX][0-9a-fA-F]*)')
INTEGER = INT_HEXADECIMAL | INT_OCTAL | INT_DECIMAL
FLOAT = Regex('[+-]?(((\d+\.\d*)|(\d*\.\d+))([eE][-+]?\d+)?)|(\d*[eE][+-]?\d+)')
LPAREN, RPAREN = Literal("(").suppress(), Literal(")").suppress()
LBRACK, RBRACK = Literal("[").suppress(), Literal("]").suppress()
LBRACE, RBRACE = Literal("{").suppress(), Literal("}").suppress()
SEMICOLON, COMMA = Literal(";").suppress(), Literal(",").suppress()
EQUAL = Literal("=").suppress()
SIZE = INTEGER | IDENTIFIER
VARNAME = IDENTIFIER
TYPENAME = IDENTIFIER
OPERATOR = oneOf("+ - * / [ ] . & ^ ! { }")
PART = nestedExpr() | nestedExpr('{','}') | IDENTIFIER | INTEGER | FLOAT | OPERATOR
EXPR = delimitedList(PART, delim=Empty()).setParseAction(keepOriginalText)
VARIABLE = (VARNAME("name") + Optional(LBRACK + SIZE + RBRACK)("size")
+ Optional(EQUAL + EXPR)("value"))
VARIABLES = delimitedList(VARIABLE.setResultsName("variables",listAllMatches=True))
DECLARATION = (TYPENAME("type") + VARIABLES + SEMICOLON)
code = """
int a[3];
int b=1, c=2.0;
float d = f(z[2], 2) + 3*g(4,a), e;
Point f = {1,2};
"""
for (token, start, end) in DECLARATION.scanString(code):
vtype = token.type
for variable in token.variables:
name = variable.name
size = variable.size
value = variable.value
s = "%s / %s" % (vtype,name)
if size: s += ' [%s]' % size[0]
if value: s += ' / %s' % value[0]
s += ";"
print s

Geotagging JPEGs with pyexiv2

I am geotagging JPEGs using the pyexiv2 Python module using code I found in another SO answer (see: What is the best way to geotag jpeg-images using Python?) and I have a question about the GPSTag value.
The code given in the answer has the following lines:
exiv_image["Exif.Image.GPSTag"] = 654
exiv_image["Exif.GPSInfo.GPSMapDatum"] = "WGS-84"
exiv_image["Exif.GPSInfo.GPSVersionID"] = '2 0 0 0'
I have looked at the Exiv2 documentation and found descriptions of GPSTag, GPSMapDatum, and GPSVersionID but am still confused about the value for GPSTag.
From the documentation it says:
A pointer to the GPS Info IFD. The Interoperability structure of the GPS Info IFD, like that of Exif IFD, has no image data.
This description does not really explain how to determine what value to use and I have not been able to find a better description of GPSTag online.
So my questions are:
Given a new image, how do you determine the value of Exif.Image.GPSTag?
Why is the code sample using a value of 654 (this may be answered by question one)?
Thanks for your help.
Best way to geotag photos using pyexiv2 is definitely with my program, GottenGeography ;-)
But seriously though, if you're wanting to access GPS data from pyexiv2, that code looks like this:
GPS = 'Exif.GPSInfo.GPS'
try:
self.latitude = dms_to_decimal(
*self.exif[GPS + 'Latitude'].value +
[self.exif[GPS + 'LatitudeRef'].value]
)
self.longitude = dms_to_decimal(
*self.exif[GPS + 'Longitude'].value +
[self.exif[GPS + 'LongitudeRef'].value]
)
except KeyError:
pass
try:
self.altitude = float(self.exif[GPS + 'Altitude'].value)
if int(self.exif[GPS + 'AltitudeRef'].value) > 0:
self.altitude *= -1
except KeyError:
pass
And writing looks like this:
self.exif[GPS + 'AltitudeRef'] = '0' if self.altitude >= 0 else '1'
self.exif[GPS + 'Altitude'] = Fraction(self.altitude)
self.exif[GPS + 'Latitude'] = decimal_to_dms(self.latitude)
self.exif[GPS + 'LatitudeRef'] = 'N' if self.latitude >= 0 else 'S'
self.exif[GPS + 'Longitude'] = decimal_to_dms(self.longitude)
self.exif[GPS + 'LongitudeRef'] = 'E' if self.longitude >= 0 else 'W'
self.exif[GPS + 'MapDatum'] = 'WGS-84'
With these support functions:
class Fraction(fractions.Fraction):
"""Only create Fractions from floats.
>>> Fraction(0.3)
Fraction(3, 10)
>>> Fraction(1.1)
Fraction(11, 10)
"""
def __new__(cls, value, ignore=None):
"""Should be compatible with Python 2.6, though untested."""
return fractions.Fraction.from_float(value).limit_denominator(99999)
def dms_to_decimal(degrees, minutes, seconds, sign=' '):
"""Convert degrees, minutes, seconds into decimal degrees.
>>> dms_to_decimal(10, 10, 10)
10.169444444444444
>>> dms_to_decimal(8, 9, 10, 'S')
-8.152777777777779
"""
return (-1 if sign[0] in 'SWsw' else 1) * (
float(degrees) +
float(minutes) / 60 +
float(seconds) / 3600
)
def decimal_to_dms(decimal):
"""Convert decimal degrees into degrees, minutes, seconds.
>>> decimal_to_dms(50.445891)
[Fraction(50, 1), Fraction(26, 1), Fraction(113019, 2500)]
>>> decimal_to_dms(-125.976893)
[Fraction(125, 1), Fraction(58, 1), Fraction(92037, 2500)]
"""
remainder, degrees = math.modf(abs(decimal))
remainder, minutes = math.modf(remainder * 60)
return [Fraction(n) for n in (degrees, minutes, remainder * 60)]
Although I am currently working on an alternative to pyexiv2 that uses GObject introspection to access the exiv2 library much more directly, called GExiv2, and would love to have some feedback on it. Both gexiv2 and pyexiv2 are wrappers around the same exiv2 library, but the difference is that pyexiv2 is a very large project with lots of glue, only works in python, and is on the brink of abandonment*; whereas gexiv2 is light and nimble, accessible from any programming language, and is well maintained thanks to it's use by Shotwell.
Hope this helps!
* pyexiv2's author, Olivier Tilloy, has asked me for help with maintainership as he no longer has much time
My version, a little lengthy...
from fractions import Fraction
import pyexiv2
try:
metadata = pyexiv2.metadata.ImageMetadata(image_file)
metadata.read();
thumb = metadata.exif_thumbnail
try:
latitude = metadata.__getitem__("Exif.GPSInfo.GPSLatitude")
latitudeRef = metadata.__getitem__("Exif.GPSInfo.GPSLatitudeRef")
longitude = metadata.__getitem__("Exif.GPSInfo.GPSLongitude")
longitudeRef = metadata.__getitem__("Exif.GPSInfo.GPSLongitudeRef")
latitude = str(latitude).split("=")[1][1:-1].split(" ");
latitude = map(lambda f: str(float(Fraction(f))), latitude)
latitude = latitude[0] + u"\u00b0" + latitude[1] + "'" + latitude[2] + '"' + " " + str(latitudeRef).split("=")[1][1:-1]
longitude = str(longitude).split("=")[1][1:-1].split(" ");
longitude = map(lambda f: str(float(Fraction(f))), longitude)
longitude = longitude[0] + u"\u00b0" + longitude[1] + "'" + longitude[2] + '"' + " " + str(longitudeRef).split("=")[1][1:-1]
latitude_value = dms_to_decimal(*metadata.__getitem__("Exif.GPSInfo.GPSLatitude").value + [metadata.__getitem__("Exif.GPSInfo.GPSLatitudeRef").value]);
longitude_value = dms_to_decimal(*metadata.__getitem__("Exif.GPSInfo.GPSLongitude").value + [metadata.__getitem__("Exif.GPSInfo.GPSLongitudeRef").value]);
print "--- GPS ---"
print "Coordinates: " + latitude + ", " + longitude
print "Coordinates: " + str(latitude_value) + ", " + str(longitude_value)
print "--- GPS ---"
except Exception, e:
print "No GPS Information!"
#print e
# Check for thumbnail
if(thumb.data == ""):
print "No thumbnail!"
except Exception, e:
print "Error processing image..."
print e;

Importing code from file into Python and adding file dialog box

I have a python script signalgen.py that plays audio using equations but I would like to be able to hard code the file where the equation is stored in eq1.txt or choose a file and import the equation.
The problems I'm having are:
1) How can I hard code a file and it's path correctly so it will play the equation as audio
I get an error
Traceback (most recent call last):
File "signalgen.py", line 484, in need_data
v += (datum * self.sig_level)
TypeError: can't multiply sequence by non-int of type 'float'
The specific block of code which I believe is causing the issue
def equation_import_function(self,t,f):
fileobj=open("/home/rat/eq1.txt","r")
eqdata =fileobj.read() #read whole file
fileobj.close()
#return math.tan(2.0*math.pi*f*t)
return eqdata
I have this line of code in the eq1.txt file-> math.tan(2.0*math.pi*f*t)
2) How can I add a file open dialog box to be able to choose a file and import the equation.
PS I'm using Ubuntu 10.04 (Linux) and the equations will be several pages long this is the reason I would like to import them into python from text files
Here's the entire code if you want to look at what I'm using below or seen on pastebin which includes line numbers http://pastebin.com/HZg0Jhaw
#!/usr/bin/env python
# -*- coding: utf-8 -*-
# ***************************************************************************
# * Copyright (C) 2011, Paul Lutus *
# * *
# * 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 2 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, write to the *
# * Free Software Foundation, Inc., *
# * 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. *
# ***************************************************************************
# version date 01-12-2011
VERSION = '1.1'
import re, sys, os
import gobject
gobject.threads_init()
import gst
import gtk
gtk.gdk.threads_init()
import time
import struct
import math
import random
import signal
import webbrowser
class Icon:
icon = [
"32 32 17 1",
" c None",
". c #2A2E30",
"+ c #333739",
"# c #464A4C",
"# c #855023",
"$ c #575A59",
"% c #676A69",
"& c #CC5B00",
"* c #777A78",
"= c #DB731A",
"- c #8A8C8A",
"; c #969895",
"> c #F68C22",
", c #A5A7A4",
"' c #F49D4A",
") c #B3B5B2",
"! c #DEE0DD",
" &&&&&&& ",
" &&&===='''''& ",
" &'''''====&'& ",
" +++++&'&&&&& &'& ",
" +#$%****&'&+ &'& ",
" +#**%$#++#&'&*#+ &'& ",
" +#**#+++++++&'&#**#+ &'& ",
" +$*$+++++++++&'&++$*$+ &'& ",
" #*#++++++++++&'&+++##&&&'& ",
" +*#++++++++#&&&'&+++#=''''& ",
" +*$++++++++#=''''&+++&'>>>'& ",
" #*+++++++++&'>>>'&+++#='''= ",
" +%$++++++++##='''=###++#&&&# ",
" +*#+++++++####&&&######++#*+ ",
" +*+++++++####++#$%$$####++*+ ",
" +*++++++##+#;,,*##*$$$###+*+ ",
" +*#++++###%!!!!,;#$*$$$###*+ ",
" +%$++++##+)!!!),-*+-%$$$#$%+ ",
" +#*+++###+-!!!,;-%#;%%$$+*#+ ",
" +*#++####+$*-*%#+*-%%$##*+ ",
" ++*#+###$$%#++#%;;*%%$#-$+ ",
" +#%+###$$%*;;;;-*%%%#**+ ",
" .+$%###$$$*******%$$*-+. ",
" .+#%%##$$*#*#%%%$%-%+. ",
" .++#%$$$$$$%%%%--#+. ",
" +++##$%*****%+++ ",
" +++++++++++++#. ",
" #--%#++#$*-%+ ",
" +%,))),;%+. ",
" ++++++. ",
" ",
" "
]
# this should be a temporary hack
class WidgetFinder:
def localize_widgets(self,parent,xmlfile):
# an unbelievable hack made necessary by
# someone unwilling to fix a year-old bug
with open(xmlfile) as f:
for name in re.findall('(?s) id="(.*?)"',f.read()):
if re.search('^k_',name):
obj = parent.builder.get_object(name)
setattr(parent,name,obj)
class ConfigManager:
def __init__(self,path,dic):
self.path = path
self.dic = dic
def read_config(self):
if os.path.exists(self.path):
with open(self.path) as f:
for record in f.readlines():
se = re.search('(.*?)\s*=\s*(.*)',record.strip())
if(se):
key,value = se.groups()
if (key in self.dic):
widget = self.dic[key]
typ = type(widget)
if(typ == list):
widget[0] = value
elif(typ == gtk.Entry):
widget.set_text(value)
elif(typ == gtk.HScale):
widget.set_value(float(value))
elif(typ == gtk.Window):
w,h = value.split(',')
widget.resize(int(w),int(h))
elif(typ == gtk.CheckButton or typ == gtk.RadioButton or typ == gtk.ToggleButton):
widget.set_active(value == 'True')
elif(typ == gtk.ComboBox):
if(value in widget.datalist):
i = widget.datalist.index(value)
widget.set_active(i)
else:
print "ERROR: reading, cannot identify key %s with type %s" % (key,type(widget))
def write_config(self):
with open(self.path,'w') as f:
for key,widget in sorted(self.dic.iteritems()):
typ = type(widget)
if(typ == list):
value = widget[0]
elif(typ == gtk.Entry):
value = widget.get_text()
elif(typ == gtk.HScale):
value = str(widget.get_value())
elif(typ == gtk.Window):
_,_,w,h = widget.get_allocation()
value = "%d,%d" % (w,h)
elif(typ == gtk.CheckButton or typ == gtk.RadioButton or typ == gtk.ToggleButton):
value = ('False','True')[widget.get_active()]
elif(typ == gtk.ComboBox):
value = widget.get_active_text()
else:
print "ERROR: writing, cannot identify key %s with type %s" % (key,type(widget))
value = "Error"
f.write("%s = %s\n" % (key,value))
def preset_combobox(self,box,v):
if(v in box.datalist):
i = box.datalist.index(v)
box.set_active(i)
else:
box.set_active(0)
def load_combobox(self,obj,data):
if(len(obj.get_cells()) == 0):
# Create a text cell renderer
cell = gtk.CellRendererText ()
obj.pack_start(cell)
obj.add_attribute (cell, "text", 0)
obj.get_model().clear()
for s in data:
obj.append_text(s.strip())
setattr(obj,'datalist',data)
class TextEntryController:
def __init__(self,parent,widget):
self.par = parent
self.widget = widget
widget.connect('scroll-event',self.scroll_event)
widget.set_tooltip_text('Enter number or:\n\
Mouse wheel: increase,decrease\n\
Shift/Ctrl/Alt: faster change')
def scroll_event(self,w,evt):
q = (-1,1)[evt.direction == gtk.gdk.SCROLL_UP]
# magnify change if shift,ctrl,alt pressed
for m in (1,2,4):
if(self.par.mod_key_val & m): q *= 10
s = self.widget.get_text()
v = float(s)
v += q
v = max(0,v)
s = self.par.format_num(v)
self.widget.set_text(s)
class SignalGen:
M_AM,M_FM = range(2)
W_SINE,W_TRIANGLE,W_SQUARE,W_SAWTOOTH,W_EQUATION_IMPORT = range(5)
waveform_strings = ('Sine','Triangle','Square','Sawtooth', 'Equation_Import')
R_48000,R_44100,R_22050,R_16000,R_11025,R_8000,R_4000 = range(7)
sample_rates = ('48000','44100','22050','16000', '11025', '8000', '4000')
def __init__(self):
self.restart = False
# exit correctly on system signals
signal.signal(signal.SIGTERM, self.close)
signal.signal(signal.SIGINT, self.close)
# precompile struct operator
self.struct_int = struct.Struct('i')
self.max_level = (2.0**31)-1
self.gen_functions = (
self.sine_function,
self.triangle_function,
self.square_function,
self.sawtooth_function,
self.equation_import_function
)
self.main_color = gtk.gdk.color_parse('#c04040')
self.sig_color = gtk.gdk.color_parse('#40c040')
self.mod_color = gtk.gdk.color_parse('#4040c0')
self.noise_color = gtk.gdk.color_parse('#c040c0')
self.pipeline = False
self.count = 0
self.imod = 0
self.rate = 1
self.mod_key_val = 0
self.sig_freq = 440
self.mod_freq = 3
self.sig_level = 100
self.mod_level = 100
self.noise_level = 100
self.enable = True
self.sig_waveform = SignalGen.W_SINE
self.sig_enable = True
self.sig_function = False
self.mod_waveform = SignalGen.W_SINE
self.mod_function = False
self.mod_mode = SignalGen.M_AM
self.mod_enable = False
self.noise_enable = False
self.sample_rate = SignalGen.R_22050
self.left_audio = True
self.right_audio = True
self.program_name = self.__class__.__name__
self.config_file = os.path.expanduser("~/." + self.program_name)
self.builder = gtk.Builder()
self.xmlfile = 'signalgen_gui.glade'
self.builder.add_from_file(self.xmlfile)
WidgetFinder().localize_widgets(self,self.xmlfile)
self.k_quit_button.connect('clicked',self.close)
self.k_help_button.connect('clicked',self.launch_help)
self.k_mainwindow.connect('destroy',self.close)
self.k_mainwindow.set_icon(gtk.gdk.pixbuf_new_from_xpm_data(Icon.icon))
self.title = self.program_name + ' ' + VERSION
self.k_mainwindow.set_title(self.title)
self.tooltips = {
self.k_sample_rate_combobox : 'Change data sampling rate',
self.k_left_checkbutton : 'Enable left channel audio',
self.k_right_checkbutton : 'Enable right channel audio',
self.k_sig_waveform_combobox : 'Select signal waveform',
self.k_mod_waveform_combobox : 'Select modulation waveform',
self.k_mod_enable_checkbutton : 'Enable modulation',
self.k_sig_enable_checkbutton : 'Enable signal',
self.k_noise_enable_checkbutton : 'Enable white noise',
self.k_mod_am_radiobutton : 'Enable amplitude modulation',
self.k_mod_fm_radiobutton : 'Enable frequency modulation',
self.k_quit_button : 'Quit %s' % self.title,
self.k_enable_checkbutton : 'Enable output',
self.k_help_button : 'Visit the %s Web page' % self.title,
}
for k,v in self.tooltips.iteritems():
k.set_tooltip_text(v)
self.config_data = {
'SampleRate' : self.k_sample_rate_combobox,
'LeftChannelEnabled' : self.k_left_checkbutton,
'RightChannelEnabled' : self.k_right_checkbutton,
'SignalWaveform' : self.k_sig_waveform_combobox,
'SignalFrequency' : self.k_sig_freq_entry,
'SignalLevel' : self.k_sig_level_entry,
'SignalEnabled' : self.k_sig_enable_checkbutton,
'ModulationWaveform' : self.k_mod_waveform_combobox,
'ModulationFrequency' : self.k_mod_freq_entry,
'ModulationLevel' : self.k_mod_level_entry,
'ModulationEnabled' : self.k_mod_enable_checkbutton,
'AmplitudeModulation' : self.k_mod_am_radiobutton,
'FrequencyModulation' : self.k_mod_fm_radiobutton,
'NoiseEnabled' : self.k_noise_enable_checkbutton,
'NoiseLevel' : self.k_noise_level_entry,
'OutputEnabled' : self.k_enable_checkbutton,
}
self.cm = ConfigManager(self.config_file,self.config_data)
self.cm.load_combobox(self.k_sig_waveform_combobox,self.waveform_strings)
self.k_sig_waveform_combobox.set_active(self.sig_waveform)
self.cm.load_combobox(self.k_mod_waveform_combobox,self.waveform_strings)
self.k_mod_waveform_combobox.set_active(self.mod_waveform)
self.cm.load_combobox(self.k_sample_rate_combobox,self.sample_rates)
self.k_sample_rate_combobox.set_active(self.sample_rate)
self.k_sig_freq_entry.set_text(self.format_num(self.sig_freq))
self.k_sig_level_entry.set_text(self.format_num(self.sig_level))
self.k_mod_freq_entry.set_text(self.format_num(self.mod_freq))
self.k_mod_level_entry.set_text(self.format_num(self.mod_level))
self.k_noise_level_entry.set_text(self.format_num(self.noise_level))
self.k_main_viewport_border.modify_bg(gtk.STATE_NORMAL,self.main_color)
self.k_sig_viewport_border.modify_bg(gtk.STATE_NORMAL,self.sig_color)
self.k_mod_viewport_border.modify_bg(gtk.STATE_NORMAL,self.mod_color)
self.k_noise_viewport_border.modify_bg(gtk.STATE_NORMAL,self.noise_color)
self.sig_freq_cont = TextEntryController(self,self.k_sig_freq_entry)
self.sig_level_cont = TextEntryController(self,self.k_sig_level_entry)
self.mod_freq_cont = TextEntryController(self,self.k_mod_freq_entry)
self.mod_level_cont = TextEntryController(self,self.k_mod_level_entry)
self.noise_level_cont = TextEntryController(self,self.k_noise_level_entry)
self.k_mainwindow.connect('key-press-event',self.key_event)
self.k_mainwindow.connect('key-release-event',self.key_event)
self.k_enable_checkbutton.connect('toggled',self.update_values)
self.k_sig_freq_entry.connect('changed',self.update_entry_values)
self.k_sig_level_entry.connect('changed',self.update_entry_values)
self.k_sig_enable_checkbutton.connect('toggled',self.update_checkbutton_values)
self.k_mod_freq_entry.connect('changed',self.update_entry_values)
self.k_mod_level_entry.connect('changed',self.update_entry_values)
self.k_noise_level_entry.connect('changed',self.update_entry_values)
self.k_sample_rate_combobox.connect('changed',self.update_values)
self.k_sig_waveform_combobox.connect('changed',self.update_values)
self.k_mod_waveform_combobox.connect('changed',self.update_values)
self.k_left_checkbutton.connect('toggled',self.update_checkbutton_values)
self.k_right_checkbutton.connect('toggled',self.update_checkbutton_values)
self.k_mod_enable_checkbutton.connect('toggled',self.update_checkbutton_values)
self.k_noise_enable_checkbutton.connect('toggled',self.update_checkbutton_values)
self.k_mod_am_radiobutton.connect('toggled',self.update_checkbutton_values)
self.cm.read_config()
self.update_entry_values()
self.update_checkbutton_values()
self.update_values()
def format_num(self,v):
return "%.2f" % v
def get_widget_text(self,w):
typ = type(w)
if(typ == gtk.ComboBox):
return w.get_active_text()
elif(typ == gtk.Entry):
return w.get_text()
def get_widget_num(self,w):
try:
return float(self.get_widget_text(w))
except:
return 0.0
def restart_test(self,w,pv):
nv = w.get_active()
self.restart |= (nv != pv)
return nv
def update_entry_values(self,*args):
self.sig_freq = self.get_widget_num(self.k_sig_freq_entry)
self.sig_level = self.get_widget_num(self.k_sig_level_entry) / 100.0
self.mod_freq = self.get_widget_num(self.k_mod_freq_entry)
self.mod_level = self.get_widget_num(self.k_mod_level_entry) / 100.0
self.noise_level = self.get_widget_num(self.k_noise_level_entry) / 100.0
def update_checkbutton_values(self,*args):
self.left_audio = self.k_left_checkbutton.get_active()
self.right_audio = self.k_right_checkbutton.get_active()
self.mod_enable = self.k_mod_enable_checkbutton.get_active()
self.sig_enable = self.k_sig_enable_checkbutton.get_active()
self.mod_mode = (SignalGen.M_FM,SignalGen.M_AM)[self.k_mod_am_radiobutton.get_active()]
self.noise_enable = self.k_noise_enable_checkbutton.get_active()
def update_values(self,*args):
self.restart = (not self.sig_function)
self.sample_rate = self.restart_test(self.k_sample_rate_combobox, self.sample_rate)
self.enable = self.restart_test(self.k_enable_checkbutton,self.enable)
self.mod_waveform = self.k_mod_waveform_combobox.get_active()
self.mod_function = self.gen_functions[self.mod_waveform]
self.sig_waveform = self.k_sig_waveform_combobox.get_active()
self.sig_function = self.gen_functions[self.sig_waveform]
self.k_sample_rate_combobox.set_sensitive(not self.enable)
if(self.restart):
self.init_audio()
def make_and_chain(self,name):
target = gst.element_factory_make(name)
self.chain.append(target)
return target
def unlink_gst(self):
if(self.pipeline):
self.pipeline.set_state(gst.STATE_NULL)
self.pipeline.remove_many(*self.chain)
gst.element_unlink_many(*self.chain)
for item in self.chain:
item = False
self.pipeline = False
time.sleep(0.01)
def init_audio(self):
self.unlink_gst()
if(self.enable):
self.chain = []
self.pipeline = gst.Pipeline("mypipeline")
self.source = self.make_and_chain("appsrc")
rs = SignalGen.sample_rates[self.sample_rate]
self.rate = float(rs)
self.interval = 1.0 / self.rate
caps = gst.Caps(
'audio/x-raw-int,'
'endianness=(int)1234,'
'channels=(int)2,'
'width=(int)32,'
'depth=(int)32,'
'signed=(boolean)true,'
'rate=(int)%s' % rs)
self.source.set_property('caps', caps)
self.sink = self.make_and_chain("autoaudiosink")
self.pipeline.add(*self.chain)
gst.element_link_many(*self.chain)
self.source.connect('need-data', self.need_data)
self.pipeline.set_state(gst.STATE_PLAYING)
def key_event(self,w,evt):
cn = gtk.gdk.keyval_name(evt.keyval)
if(re.search('Shift',cn) != None):
mod = 1
elif(re.search('Control',cn) != None):
mod = 2
elif(re.search('Alt|Meta',cn) != None):
mod = 4
else:
return
if(evt.type == gtk.gdk.KEY_PRESS):
self.mod_key_val |= mod
else:
self.mod_key_val &= ~mod
def sine_function(self,t,f):
return math.sin(2.0*math.pi*f*t)
def triangle_function(self,t,f):
q = 4*math.fmod(t*f,1)
q = (q,2-q)[q > 1]
return (q,-2-q)[q < -1]
def square_function(self,t,f):
if(f == 0): return 0
q = 0.5 - math.fmod(t*f,1)
return (-1,1)[q > 0]
def sawtooth_function(self,t,f):
return 2.0*math.fmod((t*f)+0.5,1.0)-1.0
def equation_import_function(self,t,f):
fileobj=open("/home/rat/eq1.txt","r")
eqdata =fileobj.read() #read whole file
fileobj.close()
#return math.tan(2.0*math.pi*f*t)
return eqdata
def need_data(self,src,length):
bytes = ""
# sending two channels, so divide requested length by 2
ld2 = length / 2
for tt in range(ld2):
t = (self.count + tt) * self.interval
if(not self.mod_enable):
datum = self.sig_function(t,self.sig_freq)
else:
mod = self.mod_function(t,self.mod_freq)
# AM mode
if(self.mod_mode == SignalGen.M_AM):
datum = 0.5 * self.sig_function(t,self.sig_freq) * (1.0 + (mod * self.mod_level))
# FM mode
else:
self.imod += (mod * self.mod_level * self.interval)
datum = self.sig_function(t+self.imod,self.sig_freq)
v = 0
if(self.sig_enable):
v += (datum * self.sig_level)
if(self.noise_enable):
noise = ((2.0 * random.random()) - 1.0)
v += noise * self.noise_level
v *= self.max_level
v = max(-self.max_level,v)
v = min(self.max_level,v)
left = (0,v)[self.left_audio]
right = (0,v)[self.right_audio]
bytes += self.struct_int.pack(left)
bytes += self.struct_int.pack(right)
self.count += ld2
src.emit('push-buffer', gst.Buffer(bytes))
def launch_help(self,*args):
webbrowser.open("http://arachnoid.com/python/signalgen_program.html")
def close(self,*args):
self.unlink_gst()
self.cm.write_config()
gtk.main_quit()
app=SignalGen()
gtk.main()
The imp module will help you to cleanly load Python code chunks from arbitrary files.
#!/usr/bin/env python
# equation in equation-one.py
def eqn(arg):
return arg * 3 + 2
#!/usr/bin/env python
# your code
import imp
path = "equation-one.py"
eq_mod = imp.load_source("equation", path, open(path))
print("Oh the nice stuff in eq_mod: %s" % dir(eq_mod))
In your custom function definition, you can create a file selector dialog, get the selected file path, load the code using imp, and return the result of the function inside the imported module.
I was commenting before, but I stared at your code long enough and kinda realized what you were trying to do, so it was easier for me to post an answer. Please refer to cJ Zougloubs answer as I expand on his suggestion to use the imp module.
Your equation files should implement a common interface:
# equation1.py
def eqn(*args):
return sum(*args)
Then you would load them in using cj Zougloubs suggestion, but with a common interface:
# python_rt.py
def equation_import_function(self, *args):
filepath = ''
# filepath = ... do file chooser dialog here ...
eq_mod = imp.load_source("equation", filepath)
eqdata = eq_mod.eqn(*args)
return eqdata
Now you have a function in your main code that takes any number of arguments, asks the user to pick the equation file, and gets the result for you.
Edit To address your comment more specifically
# equation1.py
import math
def eqn(*args):
f = args[0]
t = args[1]
return math.tan(2.0*math.pi*f*t)
And in your main tool, you would use imp.load_source to bring it in. Wherever you needed that equation for your audio, you could then do:
eq_mod.eqn(f, t)

Categories