These code snippets are what I'm using to interface with the C2D. This first one is the class for the C2D converter. When instantiated it performs a self calibration for gain and offset. Otherwise, the user must customize it for use.
#!/usr/bin/env python3
#
# Connections are:
# CLK => SCLK
# DOUT => MISO
# DIN => MOSI
# CS => CE0
import time
import sys
import spidev
import math
class C2D:
# commands
SelfCalibration = 0x90
SystemOffsetCal = 0xA0
SystemGainCal = 0xB0
Convert1p0 = 0x80
Convert2p5 = 0x81
Convert5p0 = 0x82
Convert10 = 0x83
# register addresses
regSTAT = 0xC0
regCTRL1 = 0xC2
regCTRL2 = 0xC4
regCTRL3 = 0xC6
regDATA = 0xC8
regSOC = 0xCA
regSGC = 0xCC
regSCOC = 0xCE
regSCGC = 0xD0
def __init__(self, debug=False):
self.spi = spidev.SpiDev()
self.spi.open(0,0)
self.spi.max_speed_hz=(1000000)
# Enable Self Calibration
self.writeReg(self.regCTRL3,[0x18])
time.sleep(0.1)
# Performing System Self Calibration.
self.command([self.SelfCalibration])
time.sleep(0.3)
def command(self, register):
self.spi.xfer(register)
return
def writeReg(self, register, dataList):
registerData = [register]
for data in dataList:
registerData.append(data)
self.spi.xfer2(registerData)
def readReg(self, register, dataList):
registerData = [register+1]
for data in dataList:
registerData.append(0)
r = self.spi.xfer2(registerData)
return r[-len(dataList):] # toss out the first byte
def twosComplement(self,data):
result = data[0] << 16 | data[1] << 8 | data[2]
if result > (1 << 23) - 1:
result = result - (1 << 24)
return result
def convert2volts(self, data):
v = data/(2**23-1) * 3.6
return v
def readADC(self):
r = self.readReg(self.regDATA,[0,0,0])
return self.twosComplement(r)
def readSelfCalOffset(self):
r = self.readReg(self.regSCOC,[0,0,0])
return self.twosComplement(r)
def readSelfCalGain(self):
r = self.readReg(self.regSCGC,[0,0,0])
return self.twosComplement(r)/2**23
def readSystemOffset(self):
r = self.readReg(self.regSOC,[0,0,0])
return int(self.twosComplement(r)/4)
def readSystemGain(self):
r = self.readReg(self.regSGC,[0,0,0])
return self.twosComplement(r)
def meanstdv(self, x):
"""
Calculate mean and standard deviation of data x[]:
mean = {\sum_i x_i \over n}
std = math.sqrt(\sum_i (x_i - mean)^2 \over n-1)
"""
n, mean, std = len(x), 0, 0
for a in x:
mean = mean + a
mean = mean / float(n)
for a in x:
std = std + (a - mean)**2
if(n > 1):
std = math.sqrt(std / float(n-1))
else:
std = 0.0
return mean, std
if __name__ == '__main__':
# instantiate C2D
cap2dig = C2D(debug=True)
print("-1 = {0}".format(cap2dig.twosComplement([0xff,0xff,0xff])))
print("1 = {0}".format(cap2dig.twosComplement([0x00,0x00,0x01])))
# set CTRL3 register
cap2dig.writeReg(cap2dig.regCTRL3,[0x18])
CTRL3 = cap2dig.readReg(cap2dig.regCTRL3,[0])
print("CTRL3 = {}".format(hex(CTRL3[0])))
#config register: SCYCLE = 1, SIGBUF = 0
cap2dig.writeReg(cap2dig.regCTRL1,[0x02])
time.sleep(1)
CTRL1 = cap2dig.readReg(cap2dig.regCTRL1, [0])
print("CTRL1 = {}".format(hex(CTRL1[0])))
print("Self Cal Offset = {0}".format(int(cap2dig.readSelfCalOffset())))
print("Self Cal Gain = {0}".format(cap2dig.readSelfCalGain()))
cap2dig.command([cap2dig.SystemOffsetCal])
time.sleep(0.5)
print("System Offset = {0}".format(cap2dig.readSystemOffset()))
print("System Gain = {0}".format(cap2dig.readSystemGain()))
result_array = []
oldSTAT = 0x00
n = 0
sd_avg2 = float(0)
try:
while True:
# start conversion
cap2dig.command([cap2dig.Convert10])
# wait for result
time.sleep(0.11)
STAT = cap2dig.readReg(cap2dig.regSTAT,[0])
if (STAT != oldSTAT):
print("STAT = {}".format(hex(STAT[0])))
oldSTAT = STAT
val = cap2dig.readADC()
print ("ADC Result: {0}".format(int(val)))
result_array.append(int(val))
if (len(result_array) == 10):
n += 1
mean,sd = cap2dig.meanstdv(result_array)
result_array = []
print("\n\tmean: {0} Counts".format(mean))
print("\tstd dev: {0:.4f} Counts".format(sd))
dnr = 20 * math.log(0.8 * 2**24/sd,10)
nfbits = math.log(0.8 * 2**24/(6 * sd),2)
print("\tDynamic Range = {0:.1f}db, ({1:.2f} bits)".format(dnr, nfbits))
sd_avg2 += sd**2
sd_avg = math.sqrt(sd_avg2/n)
print("\tAvg Std Dev = {0:.2f} Counts".format(sd_avg))
avg_dnr = 20*math.log(0.8*2**24/sd_avg,10)
avg_nfbits = math.log(0.8*2**24/(6 * sd_avg),2)
print("\tAvg Dynamic Range = {0:.1f}db, ({1:.2f} bits)\n".format(avg_dnr, avg_nfbits))
time.sleep(3)
except KeyboardInterrupt:
cap2dig.spi.close()
sys.exit(0)
This while loop is used to get the C2D data and provide it to the ringserver daemon. The C2D is instantiated as cap2dig(). Sample period for the loop is set to 0.5 seconds (2 sps) when the C2D is set to 2.5sps.
def getData():
'''
TBD
'''
global shared, seedArray, resultArray
sample_time = 0.5
# throw away first conversion result
ts = datetime.utcnow() # a timestamp for the seed file
timeNow = time.time() * 1000 # this creates a unix timestamp with millisecond resolution
cap2dig.command([cap2dig.Convert2p5])
time.sleep(sample_time)
while True:
# start next conversion
timeStart = time.time()
cap2dig.command([cap2dig.Convert2p5])
CapCount = cap2dig.readADC()
ts_next = datetime.utcnow() # a timestamp for the seed file
timeNow_next = time.time() * 1000 # this creates a unix timestamp with millisecond resolution
CapCount = cap2dig.readADC()
resultArray.append([timeNow, CapCount])
seedArray.append([ts.isoformat(),int(CapCount)]) # raw data to seedlink server
# limit array length to 1 hour
if (len(resultArray) > 7200):
# must use pop method for manager.list object.
resultArray.pop(0)
packetSize = 512 # 512 is standard packet size for mseed.
if(len(seedArray) == packetSize):
# write data stream to ascii file
#logger.info("Writing seed file.")
asciiFile = open('/home/pi/programs/Seismo/slist.ascii', 'w')
asciiFile.write("TIMESERIES EI_AEGI__BHZ, {0} samples, {2:.4f} sps, {1}, SLIST, INTEGER, Counts\n".format(packetSize, seedArray[0][0], 1/sample_time)) # write header
n = 0
for line in seedArray:
n += 1
if (n == 1):
text = repr(line[1]).rjust(10)
else:
text = text + repr(line[1]).rjust(12)
if (n == 6):
text = text + " \n"
asciiFile.write(text)
n = 0
text = text + " \n" # finish off any partial lines
asciiFile.write(text) # write the last line
asciiFile.close()
mseedFileName = seedArray[0][0].replace(":", "_")
command = 'ascii2mseed -r {0} -o /home/pi/ringserver/mseed/EI_AEGI__BHZ_{1}.mseed /home/pi/programs/Seismo/slist.ascii > /dev/null 2>&1 &'.format(packetSize,mseedFileName)
seedArray = []
os.system(command)
ts = ts_next
timeNow = timeNow_next
if (sample_time > (sample_time - time.time() + timeStart) > 0):
time.sleep(sample_time - time.time() + timeStart)
else:
logger.error("Sample timing error.")
time.sleep(sample_time)
The C2D is instructed to start a new conversion before the results of the previous conversion are captured. There should be plenty of time to obtain the ADC result before the register is overwritten with the new data. After the conversion is started all of the other housekeeping can be accomplished in the intervening 0.5 seconds. The loop is trying to keep the sample period as close to 0.5 seconds as possible. Every 512 samples -- about 4 minutes -- a miniseed file is provided to the ringserver daemon.
Discussions
Become a Hackaday.io Member
Create an account to leave a comment. Already have an account? Log In.