Table of contents
Loading the file into Numpy
iqs = np.fromfile('challenge.iq', np.complex64)
print(f"Loaded IQ data with shape: {iqs.shape}")
np.fromfile()
reads binary data from the file into a NumPy array.- The parameter
np.complex64
tells NumPy to interpret each pair of 32-bit floats as a single complex number. - The resulting shape
(3675100,)
indicates that we have about 3.6 million complex samples.
Plotting the Signal Data
fig, axs = plt.subplots(2, 2, sharex=True)
axs[0, 0].plot(np.real(iqs))
axs[0, 0].set_title("Real Part")
axs[1, 0].plot(np.imag(iqs))
axs[1, 0].set_title("Imaginary Part")
axs[0, 1].plot(np.abs(iqs))
axs[0, 1].set_title("Magnitude")
axs[1, 1].plot(np.angle(iqs))
axs[1, 1].set_title("Phase")
plt.show()
Matplotlib
Visual inspection to identify patterns and modulation types. Subplots: 2x2 grid to visualize four aspects of the signal:
- Real Part: The in-phase component.
- Imaginary Part: The quadrature component.
- Magnitude: The signal’s strength (important for distinguishing between “high” and “low” signals).
- Phase: The angle of the complex number (useful for detecting phase changes).
Magnitude -> showes “high” and “low” states.
Thresholding the Signal Magnitude
discrete = np.abs(iqs) > 0.75
threshold of 0.75 was chosen based on visual inspection of the plot
The result is a binary (True/False) array where:
True means the signal is "high" (strong).
False means the signal is "low" (weak or silent).
Grouping Consecutive True/False Values
signals = [(value, len(list(group))) for value, group in groupby(discrete)]
Group Consecutive Values: groupby() -> sequences of highs (True) and lows (False). Instead of iterating manually, groupby() gives us lengths of each group directly.
A sequence of 4994 False (silence), followed by 150 True (signal), etc.
Counting Group Sizes
sizesT = Counter(length for value, length in signals if value)
sizesF = Counter(length for value, length in signals if not value)
print(f"Sizes for True values: {sizesT}")
print(f"Sizes for False values: {sizesF}")
Counter -> collections
Frequency Analysis: We want to know which group sizes are most common for both True and False values.
This helps identify standard durations that correspond to Morse code symbols
Decoding Morse Code
def decode_morse(signals):
morse = ''.join(
('.' if length < 300 else '-') if value else (' ' if length > 2000 else '')
for value, length in signals
)
return morse
Dot vs Dash: The analysis showed that:
150±1 length groups correspond to dots (.).
500±1 length groups correspond to dashes (-).
Symbol and Letter Gaps:
1000±1 gap between symbols within a letter.
3500±2 gap between letters.
We directly map the group length to a Morse symbol or a space based on the calculated thresholds.
Print Morse Code
morse_code = decode_morse(signals)
print(f"Decoded Morse code: {morse_code}")
print("Copy the code and decode it online")
==================================================
Complete Code
import numpy as np
import matplotlib.pyplot as plt
from itertools import groupby
from collections import Counter
# Load the IQ data from the file
iqs = np.fromfile('challenge.iq', np.complex64)
print(f"Loaded IQ data with shape: {iqs.shape}")
# Plotting the IQ data: real, imaginary, magnitude, and phase
fig, axs = plt.subplots(2, 2, sharex=True)
axs[0, 0].plot(np.real(iqs))
axs[0, 0].set_title("Real Part")
axs[1, 0].plot(np.imag(iqs))
axs[1, 0].set_title("Imaginary Part")
axs[0, 1].plot(np.abs(iqs))
axs[0, 1].set_title("Magnitude")
axs[1, 1].plot(np.angle(iqs))
axs[1, 1].set_title("Phase")
plt.show()
# Thresholding the magnitude to identify signals
discrete = np.abs(iqs) > 0.75
# Grouping consecutive True/False values
signals = [(value, len(list(group))) for value, group in groupby(discrete)]
print(f"Signal groups (first 10): {signals[:10]}")
# Counting the durations of True and False groups
sizesT = Counter(length for value, length in signals if value)
sizesF = Counter(length for value, length in signals if not value)
print(f"Sizes for True values: {sizesT}")
print(f"Sizes for False values: {sizesF}")
# Decoding Morse code based on signal lengths
def decode_morse(signals):
morse = ''.join(
('.' if length < 300 else '-') if value else (' ' if length > 2000 else '')
for value, length in signals
)
return morse
morse_code = decode_morse(signals)
print(f"Decoded Morse code: {morse_code}")
# Final message
print("Copy the Morse code and decode it using an online Morse code decoder.")
$ python3 script02.py
Loaded IQ data with shape: (3675100,)
Signal groups (first 10): [(False, 4994), (True, 150), (False, 1000), (True, 500), (False, 999), (True, 150), (False, 1000), (True, 150), (False, 3498), (True, 150)]
Sizes for True values: Counter({150: 820, 500: 506, 499: 161, 149: 114, 151: 19, 501: 2})
Sizes for False values: Counter({1000: 518, 999: 475, 3498: 472, 3499: 154, 3497: 2, 4994: 1, 3489: 1})
Decoded Morse code: .-.. . -.-. --- -.. . -- --- .-. ... . .. -. - . .-. -. .- - .. --- -. .- .-.. --- ..- .-.. .- .-.. .--. .... .- -... . - -- --- .-. ... . .. -. - . .-. -. .- - .. --- -. .- .-.. . ... - ..- -. -.-. --- -.. . .--. . .-. -- . - - .- -. - -.. . - .-. .- -. ... -- . - - .-. . ..- -. - . -..- - . .- .-.. .- .. -.. . -.. . ... . .-. .. . ... -.. .. -- .--. ..- .-.. ... .. --- -. ... -.-. --- ..- .-. - . ... . - .-.. --- -. --. ..- . ... --.- ..- . .-.. .-.. . ... ... --- .. . -. - .--. .-. --- -.. ..- .. - . ... .--. .- .-. -.. . ... ... .. --. -. . ... ..- -. . .-.. ..- -- .. . .-. . ..- -. ... --- -. --- ..- ..- -. --. . ... - . ... - --- .--. -.-. . -.-. --- -.. . . ... - ... --- ..- ...- . -. - .- - - .-. .. -... ..- . .- ... .- -- ..- . .-.. -- --- .-. ... . -.-. . .--. . -. -.. .- -. - .--. .-.. ..- ... .. . ..- .-. ... -.-. --- -. - . ... - . -. - -.-. . - - . .--. .-. .. -- .- ..- - . . - - . -. -.. . -. - .- .- - - .-. .. -... ..- . .-. .-.. .- .--. .- - . .-. -. .. - . -.. ..- .-.. .- -. --. .- --. . .- ... --- -. .- ... ... .. ... - .- -. - .- .-.. ..-. .-. . -.. ...- .- .. .-.. ... - --- .--. .-.. . ..-. .-.. .- --. . ... - . -.... ---.. ----. .---- -.-. ---.. -... -... ---.. -.-. -.-. ..-. . ----. ..... .- --... -.... --... ....- ..... --... ..-. ...-- .- .---- -... --... ...-- ---.. -.... .---- ----. ..--- ---.. .- ----- ----- ..--- -.-. ..... ----- ----. ....- --... -.... ..--- -.. ..... ---.. ----- -.-. -.. --... -... -.-. ....- .- .---- ..... -.-. -.... ....- ... - --- .--. .. -. ...- . -. - . . -. .---- ---.. ...-- ..--- .--. --- ..- .-. .-.. .- - . .-.. . --. .-. .- .--. .... .. . -.-. . -.-. --- -.. .- --. . -.. . -.-. .- .-. .- -.-. - . .-. . ... .- ... ... .. --. -. . .- -.-. .... .- --.- ..- . .-.. . - - .-. . -.-. .... .. ..-. ..-. .-. . . - ... .. --. -. . -.. . .--. --- -. -.-. - ..- .- - .. --- -. ..- -. . -.-. --- -- -... .. -. .- .. ... --- -. ..- -. .. --.- ..- . -.. . ... .. --. -. .- ..- -..- .. -. - . .-. -- .. - - . -. - ... ... - --- .--. .-.. . -.-. --- -.. . -- --- .-. ... . . ... - -.-. --- -. ... .. -.. . .-. . -.-. --- -- -- . .-.. . .--. .-. . -.-. ..- .-. ... . ..- .-. -.. . ... -.-. --- -- -- ..- -. .. -.-. .- - .. --- -. ... -. ..- -- . .-. .. --.- ..- . ... ... - --- .--.
Copy the Morse code and decode it using an online Morse code decoder.
Make it minuscule as the instrcutions say:
Python
def to_minuscule():
"""
Asks the user for a string and prints it in minuscule (lowercase).
"""
input_string = input("Enter a string: ")
minuscule_string = input_string.lower()
print("Minuscule string:", minuscule_string)
to_minuscule()
(kali㉿kali)-[~/hack]
└─$ python script03.py
Enter a string: E6891C8BB8CCFE95A767457F3A1B73861928A002C5094762D580CD7BC4A15C64
Minuscule string: e6891c8bb8ccfe95a767457f3a1b73861928a002c5094762d580cd7bc4a15c64