first working

This commit is contained in:
2025-08-03 20:20:55 -04:00
commit cde56494ec
52 changed files with 1893 additions and 0 deletions

44
frametypes/__init__.py Normal file
View File

@@ -0,0 +1,44 @@
"""Frame type implementations for packet analysis."""
from .base import FrameTypeInterface
from .overview import Overview
from .chapter10 import Chapter10Stats
from .ch10_analog import Ch10AnalogStats
from .ch10_tmats import Ch10TMATSStats
from .ch10_pcm import Ch10PCMStats
from .ch10_time import Ch10TimeStats
from .iena import IENAStats
from .ptp import PTPStats
from .ptp_sync import PTPSyncStats
from .ptp_announce import PTPAnnounceStats
from .ptp_delay import PTPDelayStats
__all__ = [
'FrameTypeInterface',
'Overview',
'Chapter10Stats',
'Ch10AnalogStats',
'Ch10TMATSStats',
'Ch10PCMStats',
'Ch10TimeStats',
'IENAStats',
'PTPStats',
'PTPSyncStats',
'PTPAnnounceStats',
'PTPDelayStats',
]
# Registry of available frame type classes
FRAME_TYPES = {
'overview': Overview,
'chapter10': Chapter10Stats,
'ch10-analog': Ch10AnalogStats,
'ch10-tmats': Ch10TMATSStats,
'ch10-pcm': Ch10PCMStats,
'ch10-time': Ch10TimeStats,
'iena': IENAStats,
'ptp': PTPStats,
'ptp-sync': PTPSyncStats,
'ptp-announce': PTPAnnounceStats,
'ptp-delay': PTPDelayStats
}

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

29
frametypes/base.py Normal file
View File

@@ -0,0 +1,29 @@
from abc import ABC, abstractmethod
from typing import Dict, List, Any
from scapy.all import Packet
class FrameTypeInterface(ABC):
def __init__(self):
self.name: str = ""
self.frames_list = []
self.bytes = 0
def get_name(self):
"""Return the interface's name"""
return self.name
@abstractmethod
def add(self, timestamp: float, size: int, packet: Packet):
"""Add a packet to the statistics."""
pass
@abstractmethod
def get_summary_dict(self) -> Dict[str, Any]:
"""Return a dictionary of statistics for the tabular report."""
pass
@abstractmethod
def get_column_definitions(self) -> List[tuple]:
"""Return column definitions as [(column_name, format_spec), ...]"""
pass

56
frametypes/ch10_analog.py Normal file
View File

@@ -0,0 +1,56 @@
from collections import defaultdict
from typing import Dict, List, Any
from scapy.all import Packet
from .base import FrameTypeInterface
class Ch10AnalogStats(FrameTypeInterface):
"""Chapter 10 Analog Data Statistics"""
def __init__(self):
super().__init__()
self.name = "Chapter 10 Analog"
self.count = 0
self.bytes = 0
self.first_time = None
self.last_time = None
self.channel_ids = set()
self.sample_counts = defaultdict(int)
self.data_gaps = 0
self.overrange_count = 0
self.underrange_count = 0
def add(self, timestamp: float, size: int, packet: Packet):
self.count += 1
self.bytes += size
if self.first_time is None:
self.first_time = timestamp
self.last_time = timestamp
# In real implementation, decode Ch10 analog headers
# Extract channel ID, sample count, check for over/underrange
def get_summary_dict(self) -> Dict[str, Any]:
duration = (self.last_time or 0) - (self.first_time or 0)
return {
'Pkts': self.count,
'Bytes': self.bytes,
'Duration': round(duration, 3),
'Channels': len(self.channel_ids),
'Data Gaps': self.data_gaps,
'Overrange': self.overrange_count,
'Underrange': self.underrange_count,
'Pkt/s': round(self.count / duration, 1) if duration > 0 else 0
}
def get_column_definitions(self) -> List[tuple]:
return [
('Pkts', 'd'),
('Bytes', 'd'),
('Duration', '.3f'),
('Channels', 'd'),
('Data Gaps', 'd'),
('Overrange', 'd'),
('Underrange', 'd'),
('Pkt/s', '.1f')
]

54
frametypes/ch10_pcm.py Normal file
View File

@@ -0,0 +1,54 @@
from typing import Dict, List, Any
from scapy.all import Packet
from .base import FrameTypeInterface
class Ch10PCMStats(FrameTypeInterface):
"""Chapter 10 PCM (Pulse Code Modulation) Data Statistics"""
def __init__(self):
super().__init__()
self.name = "Chapter 10 PCM"
self.count = 0
self.bytes = 0
self.first_time = None
self.last_time = None
self.channel_ids = set()
self.frame_sync_errors = 0
self.bit_slip_events = 0
self.minor_frame_count = 0
self.major_frame_count = 0
def add(self, timestamp: float, size: int, packet: Packet):
self.count += 1
self.bytes += size
if self.first_time is None:
self.first_time = timestamp
self.last_time = timestamp
# In real implementation, decode PCM frame structure
def get_summary_dict(self) -> Dict[str, Any]:
duration = (self.last_time or 0) - (self.first_time or 0)
return {
'Pkts': self.count,
'Bytes': self.bytes,
'Duration': round(duration, 3),
'Channels': len(self.channel_ids),
'Sync Errors': self.frame_sync_errors,
'Bit Slips': self.bit_slip_events,
'Minor Frames': self.minor_frame_count,
'Major Frames': self.major_frame_count
}
def get_column_definitions(self) -> List[tuple]:
return [
('Pkts', 'd'),
('Bytes', 'd'),
('Duration', '.3f'),
('Channels', 'd'),
('Sync Errors', 'd'),
('Bit Slips', 'd'),
('Minor Frames', 'd'),
('Major Frames', 'd')
]

52
frametypes/ch10_time.py Normal file
View File

@@ -0,0 +1,52 @@
from typing import Dict, List, Any
from scapy.all import Packet
from .base import FrameTypeInterface
class Ch10TimeStats(FrameTypeInterface):
"""Chapter 10 Time Data Statistics"""
def __init__(self):
super().__init__()
self.name = "Chapter 10 Time"
self.count = 0
self.bytes = 0
self.first_time = None
self.last_time = None
self.time_sources = set()
self.time_format_changes = 0
self.leap_second_events = 0
self.time_discontinuities = 0
self.max_time_delta = 0
def add(self, timestamp: float, size: int, packet: Packet):
self.count += 1
self.bytes += size
if self.first_time is None:
self.first_time = timestamp
self.last_time = timestamp
# In real implementation, decode time packets
def get_summary_dict(self) -> Dict[str, Any]:
duration = (self.last_time or 0) - (self.first_time or 0)
return {
'Pkts': self.count,
'Bytes': self.bytes,
'Time Sources': len(self.time_sources),
'Format Changes': self.time_format_changes,
'Leap Seconds': self.leap_second_events,
'Discontinuities': self.time_discontinuities,
'Max Delta': round(self.max_time_delta, 6)
}
def get_column_definitions(self) -> List[tuple]:
return [
('Pkts', 'd'),
('Bytes', 'd'),
('Time Sources', 'd'),
('Format Changes', 'd'),
('Leap Seconds', 'd'),
('Discontinuities', 'd'),
('Max Delta', '.6f')
]

52
frametypes/ch10_tmats.py Normal file
View File

@@ -0,0 +1,52 @@
from typing import Dict, List, Any
from scapy.all import Packet
from .base import FrameTypeInterface
class Ch10TMATSStats(FrameTypeInterface):
"""Chapter 10 TMATS (Telemetry Attributes Transfer Standard) Statistics"""
def __init__(self):
super().__init__()
self.name = "Chapter 10 TMATS"
self.count = 0
self.bytes = 0
self.first_time = None
self.last_time = None
self.tmats_versions = set()
self.configuration_changes = 0
self.g_records = 0 # Data source records
self.r_records = 0 # Tape/storage records
self.m_records = 0 # Multiplexing/modulation records
def add(self, timestamp: float, size: int, packet: Packet):
self.count += 1
self.bytes += size
if self.first_time is None:
self.first_time = timestamp
self.last_time = timestamp
# In real implementation, parse TMATS records
def get_summary_dict(self) -> Dict[str, Any]:
duration = (self.last_time or 0) - (self.first_time or 0)
return {
'Pkts': self.count,
'Bytes': self.bytes,
'Versions': len(self.tmats_versions),
'Config Changes': self.configuration_changes,
'G-Records': self.g_records,
'R-Records': self.r_records,
'M-Records': self.m_records
}
def get_column_definitions(self) -> List[tuple]:
return [
('Pkts', 'd'),
('Bytes', 'd'),
('Versions', 'd'),
('Config Changes', 'd'),
('G-Records', 'd'),
('R-Records', 'd'),
('M-Records', 'd')
]

57
frametypes/chapter10.py Normal file
View File

@@ -0,0 +1,57 @@
from collections import defaultdict
from typing import Dict, List, Any
from scapy.all import Packet
from .base import FrameTypeInterface
class Chapter10Stats(FrameTypeInterface):
"""General Chapter 10 Statistics"""
def __init__(self):
super().__init__()
self.name = "Chapter 10"
self.count = 0
self.bytes = 0
self.first_time = None
self.last_time = None
self.channel_ids = set()
self.data_types = defaultdict(int)
self.sequence_errors = 0
self.last_sequence = {}
def add(self, timestamp: float, size: int, packet: Packet):
self.count += 1
self.bytes += size
if self.first_time is None:
self.first_time = timestamp
self.last_time = timestamp
# Simulate Chapter 10 specific processing
# In real implementation, decode Chapter 10 headers
if packet:
# Example: extract channel ID and data type from payload
# channel_id = extract_channel_id(packet)
# data_type = extract_data_type(packet)
# sequence = extract_sequence_number(packet)
pass
def get_summary_dict(self) -> Dict[str, Any]:
duration = (self.last_time or 0) - (self.first_time or 0)
return {
'Pkts': self.count,
'Bytes': self.bytes,
'Duration': round(duration, 3),
'Channels': len(self.channel_ids),
'Seq Errors': self.sequence_errors,
'Pkt/s': round(self.count / duration, 1) if duration > 0 else 0
}
def get_column_definitions(self) -> List[tuple]:
return [
('Pkts', 'd'),
('Bytes', 'd'),
('Duration', '.3f'),
('Channels', 'd'),
('Seq Errors', 'd'),
('Pkt/s', '.1f')
]

53
frametypes/iena.py Normal file
View File

@@ -0,0 +1,53 @@
from typing import Dict, List, Any
from scapy.all import Packet
from .base import FrameTypeInterface
class IENAStats(FrameTypeInterface):
"""IENA Protocol Statistics"""
def __init__(self):
super().__init__()
self.name = "IENA"
self.count = 0
self.bytes = 0
self.first_time = None
self.last_time = None
self.key_fields = set()
self.sequence_gaps = 0
self.last_sequence = None
def add(self, timestamp: float, size: int, packet: Packet):
self.count += 1
self.bytes += size
if self.first_time is None:
self.first_time = timestamp
self.last_time = timestamp
# Simulate IENA specific processing
if packet:
# Example: extract IENA key field and sequence
# key_field = extract_iena_key(packet)
# sequence = extract_iena_sequence(packet)
pass
def get_summary_dict(self) -> Dict[str, Any]:
duration = (self.last_time or 0) - (self.first_time or 0)
return {
'Pkts': self.count,
'Bytes': self.bytes,
'Duration': round(duration, 3),
'Key Fields': len(self.key_fields),
'Seq Gaps': self.sequence_gaps,
'Pkt/s': round(self.count / duration, 1) if duration > 0 else 0
}
def get_column_definitions(self) -> List[tuple]:
return [
('Pkts', 'd'),
('Bytes', 'd'),
('Duration', '.3f'),
('Key Fields', 'd'),
('Seq Gaps', 'd'),
('Pkt/s', '.1f')
]

64
frametypes/overview.py Normal file
View File

@@ -0,0 +1,64 @@
import statistics
from typing import Dict, List, Any
from scapy.all import Packet
from .base import FrameTypeInterface
class Overview(FrameTypeInterface):
def __init__(self):
self.name = "Overview"
self.count = 0
self.first_time = None
self.last_time = None
self.Tdeltas = []
self.bytes = 0
self.frames_list = []
def add(self, timestamp: float, size: int, packet: Packet):
self.count += 1
self.bytes += size
self.frames_list.append(packet)
if self.first_time is None:
self.first_time = timestamp
else:
self.Tdeltas.append(timestamp - self.last_time)
self.last_time = timestamp
@property
def duration(self): return (self.last_time or 0) - (self.first_time or 0)
@property
def avg_size(self): return self.bytes / self.count if self.count else 0
@property
def avg_delta(self): return statistics.mean(self.Tdeltas) if self.Tdeltas else 0
@property
def std_delta(self): return statistics.stdev(self.Tdeltas) if len(self.Tdeltas) > 1 else 0
@property
def pkt_rate(self): return self.count / self.duration if self.duration > 0 else 0
@property
def byte_rate(self): return self.bytes / self.duration if self.duration > 0 else 0
def get_summary_dict(self) -> Dict[str, Any]:
return {
'Pkts': self.count,
'Bytes': self.bytes,
'Duration': round(self.duration, 3),
'Avg Size': round(self.avg_size, 1),
'Avg TimeΔ': round(self.avg_delta, 6),
'Time 1σ': round(self.std_delta, 6),
'Pkt/s': round(self.pkt_rate, 1),
'B/s': round(self.byte_rate, 1)
}
def get_column_definitions(self) -> List[tuple]:
return [
('Pkts', 'd'),
('Bytes', 'd'),
('Duration', '.3f'),
('Avg Size', '.1f'),
('Avg Delta', '.6f'),
('Std Delta', '.6f'),
('Pkt/s', '.1f'),
('B/s', '.1f')
]

56
frametypes/ptp.py Normal file
View File

@@ -0,0 +1,56 @@
from typing import Dict, List, Any
from scapy.all import Packet
from .base import FrameTypeInterface
class PTPStats(FrameTypeInterface):
"""General PTP Statistics"""
def __init__(self):
super().__init__()
self.name = "PTP"
self.count = 0
self.bytes = 0
self.first_time = None
self.last_time = None
self.sync_messages = 0
self.follow_up_messages = 0
self.delay_req_messages = 0
self.delay_resp_messages = 0
self.announce_messages = 0
def add(self, timestamp: float, size: int, packet: Packet):
self.count += 1
self.bytes += size
if self.first_time is None:
self.first_time = timestamp
self.last_time = timestamp
# Simulate PTP message type counting
if packet:
# Example: decode PTP message type
# msg_type = extract_ptp_message_type(packet)
pass
def get_summary_dict(self) -> Dict[str, Any]:
duration = (self.last_time or 0) - (self.first_time or 0)
return {
'Pkts': self.count,
'Duration': round(duration, 3),
'Sync': self.sync_messages,
'Follow Up': self.follow_up_messages,
'Delay Req': self.delay_req_messages,
'Delay Resp': self.delay_resp_messages,
'Announce': self.announce_messages
}
def get_column_definitions(self) -> List[tuple]:
return [
('Pkts', 'd'),
('Duration', '.3f'),
('Sync', 'd'),
('Follow Up', 'd'),
('Delay Req', 'd'),
('Delay Resp', 'd'),
('Announce', 'd')
]

View File

@@ -0,0 +1,51 @@
from collections import defaultdict
from typing import Dict, List, Any
from scapy.all import Packet
from .base import FrameTypeInterface
class PTPAnnounceStats(FrameTypeInterface):
"""PTP Announce Message Statistics"""
def __init__(self):
super().__init__()
self.name = "PTP Announce"
self.count = 0
self.bytes = 0
self.first_time = None
self.last_time = None
self.announce_count = 0
self.grandmaster_changes = 0
self.priority_changes = 0
self.grandmaster_ids = set()
self.clock_classes = defaultdict(int)
def add(self, timestamp: float, size: int, packet: Packet):
self.count += 1
self.bytes += size
if self.first_time is None:
self.first_time = timestamp
self.last_time = timestamp
# In real implementation, decode PTP announce messages
def get_summary_dict(self) -> Dict[str, Any]:
duration = (self.last_time or 0) - (self.first_time or 0)
return {
'Pkts': self.count,
'Announce': self.announce_count,
'GM Changes': self.grandmaster_changes,
'Priority Changes': self.priority_changes,
'Grandmasters': len(self.grandmaster_ids),
'Clock Classes': len(self.clock_classes)
}
def get_column_definitions(self) -> List[tuple]:
return [
('Pkts', 'd'),
('Announce', 'd'),
('GM Changes', 'd'),
('Priority Changes', 'd'),
('Grandmasters', 'd'),
('Clock Classes', 'd')
]

53
frametypes/ptp_delay.py Normal file
View File

@@ -0,0 +1,53 @@
import statistics
from typing import Dict, List, Any
from scapy.all import Packet
from .base import FrameTypeInterface
class PTPDelayStats(FrameTypeInterface):
"""PTP Delay Request/Response Statistics"""
def __init__(self):
super().__init__()
self.name = "PTP Delay"
self.count = 0
self.bytes = 0
self.first_time = None
self.last_time = None
self.delay_req_count = 0
self.delay_resp_count = 0
self.pdelay_req_count = 0
self.pdelay_resp_count = 0
self.round_trip_times = []
self.asymmetry_values = []
def add(self, timestamp: float, size: int, packet: Packet):
self.count += 1
self.bytes += size
if self.first_time is None:
self.first_time = timestamp
self.last_time = timestamp
# In real implementation, match delay requests with responses
def get_summary_dict(self) -> Dict[str, Any]:
duration = (self.last_time or 0) - (self.first_time or 0)
avg_rtt = statistics.mean(self.round_trip_times) if self.round_trip_times else 0
return {
'Pkts': self.count,
'Delay Req': self.delay_req_count,
'Delay Resp': self.delay_resp_count,
'PDelay Req': self.pdelay_req_count,
'PDelay Resp': self.pdelay_resp_count,
'Avg RTT': round(avg_rtt * 1000, 3) # Convert to ms
}
def get_column_definitions(self) -> List[tuple]:
return [
('Pkts', 'd'),
('Delay Req', 'd'),
('Delay Resp', 'd'),
('PDelay Req', 'd'),
('PDelay Resp', 'd'),
('Avg RTT', '.3f')
]

58
frametypes/ptp_sync.py Normal file
View File

@@ -0,0 +1,58 @@
import statistics
from typing import Dict, List, Any
from scapy.all import Packet
from .base import FrameTypeInterface
class PTPSyncStats(FrameTypeInterface):
"""PTP Sync Message Statistics"""
def __init__(self):
super().__init__()
self.name = "PTP Sync"
self.count = 0
self.bytes = 0
self.first_time = None
self.last_time = None
self.sync_count = 0
self.follow_up_count = 0
self.two_step_count = 0
self.one_step_count = 0
self.sync_intervals = []
self.clock_ids = set()
def add(self, timestamp: float, size: int, packet: Packet):
self.count += 1
self.bytes += size
if self.first_time is None:
self.first_time = timestamp
else:
if self.last_time and self.sync_count > 0:
self.sync_intervals.append(timestamp - self.last_time)
self.last_time = timestamp
# In real implementation, decode PTP sync messages
def get_summary_dict(self) -> Dict[str, Any]:
duration = (self.last_time or 0) - (self.first_time or 0)
avg_interval = statistics.mean(self.sync_intervals) if self.sync_intervals else 0
return {
'Pkts': self.count,
'Sync Msgs': self.sync_count,
'Follow-ups': self.follow_up_count,
'Two-step': self.two_step_count,
'One-step': self.one_step_count,
'Clock IDs': len(self.clock_ids),
'Avg Interval': round(avg_interval, 6)
}
def get_column_definitions(self) -> List[tuple]:
return [
('Pkts', 'd'),
('Sync Msgs', 'd'),
('Follow-ups', 'd'),
('Two-step', 'd'),
('One-step', 'd'),
('Clock IDs', 'd'),
('Avg Interval', '.6f')
]