commit 71a97fc22662739d2f00cdc9f3118807f11b9193 Author: noisedestroyers Date: Thu Jul 24 16:59:52 2025 +0000 init from existing diff --git a/ch10ConsumeStream.py b/ch10ConsumeStream.py new file mode 100644 index 0000000..b3ccfb8 --- /dev/null +++ b/ch10ConsumeStream.py @@ -0,0 +1,263 @@ +from ast import Continue +import socket +import sys +import platform +import binascii +import datetime +import csv + +from queue import Queue +import threading +import time + +import matplotlib.pyplot as plt +import matplotlib.animation as animation +import numpy as np + +class CH10Packet: + def __init__(self, packetData): + offset = 0; + self.msgFormat = packetData[offset] & 0x0F + self.msgType = (packetData[offset] >> 4) & 0x0F + offset += 1 + self.msgSeqNo = (packetData[offset+2] << 16) | (packetData[offset+1] << 8) | (packetData[offset]) + offset += 3 + self.uSync = (packetData[offset+1] << 8) | (packetData[offset]) + offset += 2 + self.channelID = (packetData[offset+1] << 8) | (packetData[offset]) + offset += 2 + self.pktLen = (packetData[offset+3] << 24) | (packetData[offset+2] << 16) | (packetData[offset+1] << 8) | (packetData[offset]) + offset += 4 + self.dataLen = (packetData[offset+3] << 24) | (packetData[offset+2] << 16) | (packetData[offset+1] << 8) | (packetData[offset]) + offset += 4 + self.dataTypeVer = (packetData[offset]) + offset += 1 + self.seqNumber = (packetData[offset]) + offset += 1 + self.pktFlags = (packetData[offset]) + offset += 1 + self.dataType = (packetData[offset]) + + self.bIncludesSecondaryTimeHeader = False + if (self.pktFlags / 128.0 >= 1.0): + self.bIncludesSecondaryTimeHeader = True + + if self.dataType != 0X21: + return + + if self.dataType == 0x00 or self.dataType == 0x01: + return + + offset += 1 + self.aubyRelTime = (packetData[offset+5] << 40) | (packetData[offset+4] << 32) | (packetData[offset+3] << 24) | (packetData[offset+2] << 16) | (packetData[offset+1] << 8) | (packetData[offset]) + + offset += 6 + self.checksum = (packetData[offset+1] << 8) | (packetData[offset]) + + offset += 2 + if(self.bIncludesSecondaryTimeHeader): + self.secondaryTimeNs = (packetData[offset+3] << 24) | (packetData[offset+2] << 16) | (packetData[offset+1] << 8) | (packetData[offset]) + offset += 4 + self.secondaryTimeSec = (packetData[offset+3] << 24) | (packetData[offset+2] << 16) | (packetData[offset+1] << 8) | (packetData[offset]) + offset += 4 + offset += 2 #rsv + self.checksumSecondary = (packetData[offset+1] << 8) | (packetData[offset]) + offset += 2 + + self.Ch10AnalogMode = packetData[offset] & 0x03 + self.Ch10AnalogLength = (packetData[offset] >> 2) & 0x3F + offset += 1 + self.Ch10AnalogSubChannelID = (packetData[offset]) + offset += 1 + self.Ch10AnalogSubChannelCount = (packetData[offset]) + offset += 1 + self.Ch10AnalogFactor = packetData[offset] & 0x0F + self.Ch10AnalogSAME = (packetData[offset] >> 4) & 0x0F + offset += 1 + + self.bIsTSRAIR = False + if(self.Ch10AnalogSubChannelCount==18): + self.bIsTSRAIR = True + + self.sampleData = list() + self.sampleCount = ((self.dataLen - 4) / self.Ch10AnalogSubChannelCount)/2.0 + i = 1 + while i <= self.sampleCount: + thisSample = list() + ii = 1 + if(self.bIncludesSecondaryTimeHeader): + val = float(self.secondaryTimeSec) + (float(self.secondaryTimeNs)/1000000000.0) + (i/8000.0) + + thisSample.append(val) + # datetime.datetime.utcfromtimestamp(self.secondaryTimeSec) + # .strftime('%Y-%m-%d %H:%M:%S') + f':{self.secondaryTimeNs}') + else: + thisSample.append(self.aubyRelTime) + + while ii <= self.Ch10AnalogSubChannelCount: + thisSample.append(toSigned16(self.bIsTSRAIR,(packetData[offset+1] << 8) | (packetData[offset]))) + offset+=2 + ii += 1 + + self.sampleData.append(thisSample) + i += 1 + +class CH10TMATPacket: + def __init__(self, packetData): + offset = 0; + self.Version = packetData[offset] & 0xFF + if self.Version != 0x11: + return + offset += 1 + self.SetupRecordConfigurationChange = (packetData[offset]) & 0x01 + self.Format = (packetData[offset] & 0x02) >> 1 + offset = 40 + self.tmats = packetData[offset:len(packetData)+1].decode("utf-8") + +multicast_group = '239.1.2.10' +interface_ip = '192.168.4.199' +multicast_port_range = [8400] +fig, ax = plt.subplots() +line, = ax.plot([], []) # Empty line object for updating data +lines = [line] + +data_queue = Queue() +plotwidth = 50000 +xdata = [] +ydata = [] + +def toSigned16(bIsTSRAIR, n): + if bIsTSRAIR: + n = n & 0xffff + return (n ^ 0x8000) - 0x8000 + else: + return n - 0x8000 + +def create_socket(interface_ip, multicast_port, multicast_group): + sock = socket.socket(socket.AF_INET, socket.SOCK_DGRAM, socket.IPPROTO_UDP) + sock.setsockopt(socket.IPPROTO_IP, socket.IP_MULTICAST_TTL, 32) + + # linux binds to multicast address, windows to interface address + ip_bind = interface_ip if platform.system() == "Windows" else multicast_group + sock.bind((ip_bind, multicast_port)) + + # socket.IPPROTO_IP works on Linux and Windows + sock.setsockopt(socket.IPPROTO_IP, socket.IP_MULTICAST_IF, socket.inet_aton(interface_ip)) + sock.setsockopt(socket.IPPROTO_IP, socket.IP_ADD_MEMBERSHIP, socket.inet_aton(multicast_group) + socket.inet_aton(interface_ip)) + return sock + +def initPlot(): + global xdata + global ydata + ax.set_xlim(0, plotwidth) + ax.set_ylim(-32767, 32767) + line.set_data([], []) + line.set_linewidth(1) + ax.autoscale(axis='y') + + xdata = [i * 1.0 for i in range(plotwidth)] + ydata = [0.0 for i in range(plotwidth)] + + return line, + +def animatePlot(frame): + global xdata + global ydata + items = [] + + while data_queue.qsize() > 0: + items.append(data_queue.get_nowait()) + + times = [np.floor(r[0]*1000) / 1000 for r in items] + xdata.extend(times) + xdata = xdata[0-plotwidth:] + + new = [r[1] for r in items] + ydata.extend(new) + ydata = ydata[0-plotwidth:] + + ax.set_xlim(min(xdata),max(xdata)) + # ax.set_autoscalex_on(True) + + line.set_data(xdata, ydata) + + return line, + +def read_packets(): + read_list = [] + for multicast_port in multicast_port_range: + read_list.append(create_socket(interface_ip, multicast_port, multicast_group)) + bDisplayPacketMetadata = False + bDisplayPacketData = False + bDisplayPacketTmats = True + while True: + for sock in read_list: + #print(f'Reading --- {sock.getsockname()[1]}') + data, srcAddress = sock.recvfrom(1500) + + ch10packet = CH10Packet(data) + # tmatsPacket = CH10TMATPacket(data) + #print(f'Received packet of {len(data)} bytes from {srcAddress[0]}:{sock.getsockname()[1]}') + if ch10packet.dataType == 0X21: + for i in ch10packet.sampleData: + data_queue.put(i) + + if bDisplayPacketData: + my_array = np.array(ch10packet.sampleData) + + hexdata = binascii.hexlify(my_array, " ", 2) + #hexdata = ''.join(['{:02x} '.format(b) for b in ch10packet.sampleData]) + print('Data = %s' % hexdata) + + if bDisplayPacketMetadata: + print(f'msgFormat: {ch10packet.msgFormat}') + print(f'msgType: {ch10packet.msgType}') + print(f'msgSeqNo: {ch10packet.msgSeqNo}') + print(f'uSync: {ch10packet.uSync}') + print(f'channelID: {ch10packet.channelID}') + print(f'pktLen: {ch10packet.pktLen}') + print(f'dataLen: {ch10packet.dataLen}') + print(f'dataTypeVer: {ch10packet.dataTypeVer}') + print(f'seqNumber: {ch10packet.seqNumber}') + print(f'pktFlags: {ch10packet.pktFlags}') + print(f'dataType: {ch10packet.dataType}') + print(f'aubyRelTime: {ch10packet.aubyRelTime}') + print(f'checksum: {ch10packet.checksum}') + if ch10packet.bIncludesSecondaryTimeHeader: + print(f'secondaryTimeNs: {ch10packet.secondaryTimeNs}') + print(f'secondaryTimeSec: {ch10packet.secondaryTimeSec}') + print('UTC Time:',datetime.datetime.utcfromtimestamp(ch10packet.secondaryTimeSec).strftime('%Y-%m-%d %H:%M:%S'), f':{ch10packet.secondaryTimeNs}') + print(f'Ch10AnalogMode: {ch10packet.Ch10AnalogMode}') + print(f'Ch10AnalogLength: {ch10packet.Ch10AnalogLength}') + print(f'Ch10AnalogSubChannelID: {ch10packet.Ch10AnalogSubChannelID}') + print(f'Ch10AnalogSubChannelCount: {ch10packet.Ch10AnalogSubChannelCount}') + print(f'Ch10AnalogFactor: {ch10packet.Ch10AnalogFactor}') + print(f'Ch10AnalogSAME: {ch10packet.Ch10AnalogSAME}') + print(f'Channel 1: {toSigned16(ch10packet.bIsTSRAIR,ch10packet.sampleData[0][1])}') + + # with open(f'{srcAddress[0]}.{sock.getsockname()[1]}.{epochTimeNow}.csv', 'a', newline='') as csvfile: + # wr = csv.writer(csvfile, quoting=csv.QUOTE_ALL) + # for sample in ch10packet.sampleData: + # wr.writerow(sample) +def main(): + epochTimeNow = (datetime.datetime.now(datetime.timezone.utc) - datetime.datetime(1970, 1, 1, tzinfo=datetime.timezone.utc)).total_seconds() + consumer_thread = threading.Thread(target=read_packets) + consumer_thread.daemon = True + consumer_thread.start() + + time.sleep(0.5) + + ani = animation.FuncAnimation( + fig, animatePlot, + init_func=initPlot, + interval=20, + # blit=True, + cache_frame_data=False + ) + + + plt.show() + # input('Press enter to exit') + +if __name__ == '__main__': + main() diff --git a/chapter10.py b/chapter10.py new file mode 100644 index 0000000..a8037a1 --- /dev/null +++ b/chapter10.py @@ -0,0 +1,106 @@ +class CH10Packet: + def __init__(self, packetData): + offset = 0; + self.msgFormat = packetData[offset] & 0x0F + self.msgType = (packetData[offset] >> 4) & 0x0F + offset += 1 + self.msgSeqNo = (packetData[offset+2] << 16) | (packetData[offset+1] << 8) | (packetData[offset]) + offset += 3 + self.uSync = (packetData[offset+1] << 8) | (packetData[offset]) + offset += 2 + self.channelID = (packetData[offset+1] << 8) | (packetData[offset]) + offset += 2 + self.pktLen = (packetData[offset+3] << 24) | (packetData[offset+2] << 16) | (packetData[offset+1] << 8) | (packetData[offset]) + offset += 4 + self.dataLen = (packetData[offset+3] << 24) | (packetData[offset+2] << 16) | (packetData[offset+1] << 8) | (packetData[offset]) + offset += 4 + self.dataTypeVer = (packetData[offset]) + offset += 1 + self.seqNumber = (packetData[offset]) + offset += 1 + self.pktFlags = (packetData[offset]) + offset += 1 + self.dataType = (packetData[offset]) + + self.bIncludesSecondaryTimeHeader = False + if (self.pktFlags / 128.0 >= 1.0): + self.bIncludesSecondaryTimeHeader = True + + if self.dataType != 0X21: + return + + if self.dataType == 0x00 or self.dataType == 0x01: + return + + offset += 1 + self.aubyRelTime = (packetData[offset+5] << 40) | (packetData[offset+4] << 32) | (packetData[offset+3] << 24) | (packetData[offset+2] << 16) | (packetData[offset+1] << 8) | (packetData[offset]) + + offset += 6 + self.checksum = (packetData[offset+1] << 8) | (packetData[offset]) + + offset += 2 + if(self.bIncludesSecondaryTimeHeader): + self.secondaryTimeNs = (packetData[offset+3] << 24) | (packetData[offset+2] << 16) | (packetData[offset+1] << 8) | (packetData[offset]) + offset += 4 + self.secondaryTimeSec = (packetData[offset+3] << 24) | (packetData[offset+2] << 16) | (packetData[offset+1] << 8) | (packetData[offset]) + offset += 4 + offset += 2 #rsv + self.checksumSecondary = (packetData[offset+1] << 8) | (packetData[offset]) + offset += 2 + + self.Ch10AnalogMode = packetData[offset] & 0x03 + self.Ch10AnalogLength = (packetData[offset] >> 2) & 0x3F + offset += 1 + self.Ch10AnalogSubChannelID = (packetData[offset]) + offset += 1 + self.Ch10AnalogSubChannelCount = (packetData[offset]) + offset += 1 + self.Ch10AnalogFactor = packetData[offset] & 0x0F + self.Ch10AnalogSAME = (packetData[offset] >> 4) & 0x0F + offset += 1 + + self.bIsTSRAIR = False + if(self.Ch10AnalogSubChannelCount==18): + self.bIsTSRAIR = True + + self.sampleData = list() + self.sampleCount = ((self.dataLen - 4) / self.Ch10AnalogSubChannelCount)/2.0 + i = 1 + while i <= self.sampleCount: + thisSample = list() + ii = 1 + if(self.bIncludesSecondaryTimeHeader): + val = float(self.secondaryTimeSec) + (float(self.secondaryTimeNs)/1000000000.0) + (i/8000.0) + + thisSample.append(val) + # datetime.datetime.utcfromtimestamp(self.secondaryTimeSec) + # .strftime('%Y-%m-%d %H:%M:%S') + f':{self.secondaryTimeNs}') + else: + thisSample.append(self.aubyRelTime) + + while ii <= self.Ch10AnalogSubChannelCount: + thisSample.append(toSigned16(self.bIsTSRAIR,(packetData[offset+1] << 8) | (packetData[offset]))) + offset+=2 + ii += 1 + + self.sampleData.append(thisSample) + i += 1 + +class CH10TMATPacket: + def __init__(self, packetData): + offset = 0; + self.Version = packetData[offset] & 0xFF + if self.Version != 0x11: + return + offset += 1 + self.SetupRecordConfigurationChange = (packetData[offset]) & 0x01 + self.Format = (packetData[offset] & 0x02) >> 1 + offset = 40 + self.tmats = packetData[offset:len(packetData)+1].decode("utf-8") + +def toSigned16(bIsTSRAIR, n): + if bIsTSRAIR: + n = n & 0xffff + return (n ^ 0x8000) - 0x8000 + else: + return n - 0x8000 \ No newline at end of file