Reverse engineering my cable modem and turning it into an SDR

Challenge maintained by stdw
Hosted on GitHub Pages — Theme by mattgraham
Introduction
Just a few weeks in the past I obtained interested by an previous cable modem sitting in my closet,
a Motorola MB7220. Initially I used to be serious about what sort of {hardware} it had
and if it was working Linux. Some fast looking out introduced me to a thread on
an internet discussion board the place folks had been discussing the in-built spectrum analyzer function
used for diagnostics. Somebody talked about that they may see spikes
similar to FM radio stations. This sparked a thought: if a cable modem
and a digital TV tuner dongle are basically doing the identical factor (receiving
and demodulating QAM indicators), may a modem be became an
SDR (software-defined radio)
a la RTL-SDR?
Going into this undertaking, I knew subsequent to nothing about RF and had no thought if
this purpose was even possible in any respect for the {hardware}. I discovered
an SDR project primarily based on an Analog Units
cable modem chip, in addition to a forum thread
the place another person was questioning about the identical factor a couple of years in the past.
The final submit within the thread from person VK4HAT states:
I say when you have the talents, time and want, give it a go and see the place you find yourself. If google reveals nothing, then its possible not been tried. With so few firsts obtainable in life, take people who current themselves and have a crack, even when failure is all the time an possibility.
So that’s precisely what I did.
Gaining Entry
My first purpose was to search for an entry vector or a strategy to talk with the
machine. I knew that there wasn’t a lot to see on the net interface and telnet
was disabled, so I skipped forward to opening it up.
After eradicating a couple of screws from the plastic housing to get entry to the
board, my first thought was to search for UART headers to take a peek on the serial console.
After figuring out two candidates consisting of 4 vias surrounded by a
rectangle close to the sting of the PCB, it was time to determine the pins.
Utilizing a multimeter, the bottom pin will be simply recognized by checking
the continuity with one of many steel shields on board. The VCC pin will be
recognized by measuring the voltage of every pin when powering on the board.
It must be a gentle 3.3v, or in some circumstances 1.8v or 5v. This pin will not be
wanted, however remains to be helpful to determine the working voltage and remove
one candidate for the Tx and Rx pins.
Whereas booting, the Tx pin will sit on common slightly decrease than the VCC pin
and drop a lot decrease when numerous information is being output. This leaves the final
pin as Rx.
One of many UARTs recognized earlier didn’t appear to be transmitting something
whereas the opposite did. After soldering some wires to the energetic UART, I linked
the Tx to UART Rx GPIO pin on a Raspberry Pi, the Rx to the Pi’s Tx, and the
floor to the bottom pin. Observe that this could solely be carried out as a result of each programs
are 3.3v. Had that not been the case, a USB TTL adapter with an adjustable
voltage stage may very well be used simply as simply, and might be a greater thought most
of the time anyway.
There are a couple of explanation why the Raspberry Pi will not be the perfect serial interface
resembling in the event you want parity or different options, however on this case I had it available
and it really works. The serial console of the Pi should even be disabled in order that it may
be freed up for different functions. There may be another excuse I selected to make use of the
Raspberry Pi which I’ll get to later.
Lastly, to truly see the information I used the cu
utility:cu -l /dev/serial0 -s 115200
The baud price was a fortunate guess, however 115200 is quite common on such gadgets.
If the baud price is mistaken you’ll rapidly know once you see a bunch of rubbish
on the display. A logic analyzer may very well be used to definitively discover the baud
price and different parameters, however guessing is usually faster and all the time
cheaper.
After powering on the machine, the terminal full of output:
pi@raspberrypi:~/modem $ cu -l /dev/serial0 -s 115200
Related.
�
B3312inim S C 84(9 m
ose_VS 8
STesldlo rh 83 rs 10
STesldhi: _h 8, _s 13
Sync: 0
MemSize: 128 M
Chip ID: BCM3383D-B0
BootLoader Model: 2.4.0 fyl spiboot diminished DDR drive avs
Construct Date: Nov 12 2015
Construct Time: 14:31:43
SPI flash ID 0xef4016, dimension 4MB, block dimension 64KB, write buffer 256, flags 0x0
Cust key dimension 128
Signature/PID: 3383
Picture 1 Program Header:
Signature: 3383
Management: 0005
Main Rev: 0003
Minor Rev: 0000
Construct Time: 2015/11/26 08:47:57 Z
File Size: 1692841 bytes
Load Deal with: 80004000
Filename: ecram_sto.bin
HCS: e749
CRC: 175b753f
Discovered picture 1 at offset 20000
Enter '1', '2', or 'p' inside 2 seconds or take default...
Performing CRC on Picture 1...
CRC time = 282177012
Detected LZMA compressed picture... decompressing...
Goal Deal with: 0x80004000
decompressSpace is 0x8000000
Elapsed time 736066500
Decompressed size: 8091524
Executing Picture 1...
eCos - hal_diag_init
Ecos reminiscence map:
BLOCK OWNER MIPS SIZE MEM
Block 0: Proprietor: 0 - 0x00000000 0x07e00000 0x00000000
Block 0: Proprietor: 0 - 0 MB 126 MB 0 MB
Block 1: Proprietor: 3 - 0x07e00000 0x00200000 0x07e00000
Block 1: Proprietor: 3 - 126 MB 2 MB 126 MB
126MB (129024KB) remaining for eCos
Init machine '/dev/BrcmTelnetIoDriver'
Init machine '/dev/ttydiag'
Init tty channel: 807bb020
Init machine '/dev/tty0'
Init tty channel: 807bb040
Init machine '/dev/haldiag'
HAL/diag SERIAL init
Init machine '/dev/ser0'
BCM 33XX SERIAL init - dev: b4e00500.2
Set output buffer - buf: 0x80852408 len: 4096
Set enter buffer - buf: 0x80853408 len: 4096
BCM 33XX SERIAL config
Init machine '/dev/ser1'
BCM 33XX SERIAL init - dev: b4e00520.3
Set output buffer - buf: 0x80854408 len: 4096
Set enter buffer - buf: 0x80855408 len: 4096
BCM 33XX SERIAL config
Init machine '/dev/ser2'
InitBoard: MIPS frequency 637200000
...
Studying Everlasting settings from non-vol...
Checksum for everlasting settings: 0xe9d88f65
Setting downstream calibration signature to '5.7.1mp1|die temperature:70.775degC'
Settings had been learn and verified.
Studying Dynamic settings from non-vol...
Checksum for dynamic settings: 0x6e4a329
Settings had been learn and verified.
Console enter has been disabled in non-vol.
Console output has been disabled in non-vol! Goodbye...
[00:00:00 01/01/1970] [Reset/Standby Switch Thread] BcmResetStandbySwitchThread::ProcessResetSwitchEvent: (Reset/Standby Change Thread) Reset change launched; resetting...
[00:00:00 01/01/1970] [Reset/Standby Switch Thread] BcmResetStandbySwitchThread::ProcessResetSwitchEvent: (Reset/Standby Change Thread) Cant Reset pfCmDocsisCtlThread==NULL...
This output comprises a wealth of knowledge. The machine is
working eCos on a MIPS processor
which is a part of a Broadcom BCM3383 SoC. It turns on the market are literally
two MIPS processors on this SoC though one among them will not be used on this
modem, explaining the opposite UART. On some gadgets, the second processor
will run Linux for extra options.
Additionally, this looks like the top of the road for serial as a result of shortly after
booting the precise OS, it disables the serial console. Hitting “p” on the
bootloader immediate doesn’t result in a lot besides a strategy to obtain new OS
photos by way of tftp and a utility to learn and write reminiscence addresses. This might
be used to bypass the verify, however a a lot higher understanding of the OS and
reminiscence format could be required.
Dumping the flash
My purpose now was to allow the serial console. Examination of the board reveals
a single SPI flash
chip which possible comprises the bootloader, OS, and configuration as it’s the
solely non-volatile storage seen on the board.
That is the place the Raspberry Pi turns out to be useful as soon as once more. The GPIO header additionally
conveniently comprises a SPI interface which can be utilized to learn the information off
of the flash chip.
Looking out the quantity on the chip, “winbond 25Q32JV”, yields the datasheet
containing the pinout. The vital ones are VCC, Chip Choose (CS), Clock
(CLK), Information Out (DO), Information In (DI), and floor.
One frequent difficulty with dumping a SPI chip on a board is that the chip requires
energy, however this will even often energy the board and trigger it to start out booting
and utilizing the chip. I selected to beat this by heating the VCC pin with my
soldering iron and really rigorously lifting it off the pad. It is a handy,
however slightly crude resolution which can end in snapped off leads so use at your
personal danger! I additionally soldered a jumper wire to the pad and one other to the floating
leg in order that I may simply join and disconnect them and permit the machine to
boot once more.
One other be aware, on some boards the Chip Choose pin is assumed to all the time be
enabled so it’s immediately tied to VCC. This implies once you energy the CS
pin, the board additionally begins booting. This may be solved in an identical manner
to the VCC pin.
Now, wires will be soldered to the remainder of the pins and the they are often
linked to the Raspberry Pi. The bottom goes to floor (the UART floor
from earlier will also be used), the VCC to the Pi’s 3.3v pin. (Once more, it’s
crucial to confirm with the datasheet that this can be a 3.3v chip as a result of the Pi
solely helps 3.3v). The DO pin is linked to the Pi’s SPI MISO
(grasp in
slave out) pin and DI to the MOSI
pin (grasp out slave in). Lastly, the
Clock is linked to the SCLK
GPIO pin and the Chip Choose to the CE0
pin.
![]() |
---|
Not the perfect soldering job however it can work |
To really learn the chip, there’s a incredible instrument referred to as
flashrom which helps an infinite variety of
chips. flashrom
is current within the repos of many distributions together with that
of the Raspberry Pi OS (previously generally known as Raspbian).
Fortunately the W25Q32JV is supported, underneath the title “W25Q32.V”. A fast verify on
the flashrom wiki reveals the scale and voltage match what is anticipated and that
the chip is absolutely supported.
Earlier than continuing, be certain that the SPI interface on the Pi is enabled by
utilizing the raspi-config
utility and checking underneath “Interfacing Choices”.
Eventually we will learn the chip. First confirm that it’s detected and every little thing is
wired accurately:
flashrom -p linux_spi:dev=/dev/spidev0.0,spispeed=2000
--chip W25Q32.V
If that succeeds we will now dump the contents:
flashrom -p linux_spi:dev=/dev/spidev0.0,spispeed=2000
--chip W25Q32.V
--read modem.bin
Firmware Evaluation
A fast look on the hexdump reveals that a lot of the information is compressed or
encrypted however close to the top, the configuration is seen.
...
003f00c0: ffff ffff ffff ffff ffff 0000 07b0 369a ..............6.
003f00d0: 6336 0010 434d 4170 0002 0000 0002 0000 c6..CMAp........
003f00e0: 0000 0057 4d4c 6f67 0005 0004 7573 6572 ...WMLog....person
003f00f0: 0004 7573 6572 0005 6164 6d69 6e00 086d ..person..admin..m
003f0100: 6f74 6f72 6f6c 6102 7465 6368 6e69 6369 otorola.technici
...
The online interface credentials are plainly seen in addition to many different
encoded configuration values.
After some looking out, I got here throughout an excellent undertaking referred to as
bcm2-utils which comprises utilities
to dump, parse, and modify the configuration on Broadcom cable modems. The repo
additionally comprises numerous very detailed documentation on the format of the
firmware and configuration. As a fast be aware, the dumping function requires
both telnet or serial connection, neither of which had been obtainable on my machine.
The beginning of the configuration is definitely at 0x003f0000 on my machine and
consists of the 202 0xff bytes. After extracting simply the config from the dump,
I used to be in a position to efficiently learn it with bcm2cfg
.
I enabled the serial console in addition to telnet entry and set an easier telnet
password.
$ ./bcm2-utils/bcm2cfg set bfc.serial_console_mode "rw"
bfc.serial_console_mode = rw
$ ./bcm2-utils/bcm2cfg set userif.remote_acc_methods 0x3
userif.remote_acc_methods = http | telnet
$ ./bcm2-utils/bcm2cfg set userif.remote_acc_pass abcd
userif.remote_acc_pass = abcd
Subsequent, I appended the modified configuration onto a file padded with zeros up
to the suitable offset and used flashrom
to write down the configuration again
to the chip.
To keep away from rewriting the whole chip, I created a format file for flashrom
so it
would solely overwrite the configuration slightly than the whole contents which is
why the modified picture is simply padded with zeros.
The format file appears to be like like this:
00000000:003effff fw
003f0000:003fffff cfg
and the command:
flashrom -p linux_spi:dev=/dev/spidev0.0,spispeed=2000
--chip W25Q32.V
--layout ./format
--image cfg
--write modem-modified.bin
After opening the serial console and booting once more, I’m greeted with… the
very same factor as earlier than.
Nearer examination of the flash picture reveals that there are a selection of
repeated copies of the configuration following the preliminary one. These have
slight variations, the obvious being extra error log messages. It
seems that the machine retains rolling backups of the configuration in case
one will get corrupted.
To simplify issues, I did a manufacturing unit reset via the net interface to get rid
of all of the error log messages. Then I dumped the flash once more and repeated the
earlier course of to switch the clear configuration besides this time I truncated
the config to solely embody the primary copy. Then utilizing some dd
instructions I
would slightly not take into consideration, I reconstructed the whole configuration part,
appending the modified config at every offset the place a replica of the config
began.
After flashing this new picture and booting as soon as once more, I’m lastly in a position to
view the whole boot log after which have entry to a console, though the
output is being flooded by a course of on the lookout for a sign.
...
Studying Everlasting settings from non-vol...
Checksum for everlasting settings: 0xe9d88f65
Setting downstream calibration signature to '5.7.1mp1|die temperature:70.775degC^@^@^@^@^@'
Settings had been learn and verified.
Studying Dynamic settings from non-vol...
Checksum for dynamic settings: 0x2630e508
Settings had been learn and verified.
[00:00:00 01/01/1970] [tStartup] BcmBfcFpmDriver::Init: (BFC FPM Driver) Setting FPM Buffer dimension to: 256 Base Deal with: 0x87566600
[00:00:00 01/01/1970] [tStartup] BcmBfcFpmDriver::Init: (BFC FPM Driver) fFpmLargestBufferSize: 2048 fFpmSizeShiftBits: 0x8
[00:00:00 01/01/1970] [tStartup] BcmBfcFpmDriver::Init: (BFC FPM Driver) Pool index: 0 pool dimension: 2048
[00:00:00 01/01/1970] [tStartup] BcmBfcFpmDriver::Init: (BFC FPM Driver) Pool index: 1 pool dimension: 1024
[00:00:00 01/01/1970] [tStartup] BcmBfcFpmDriver::Init: (BFC FPM Driver) Pool index: 2 pool dimension: 512
[00:00:00 01/01/1970] [tStartup] BcmBfcFpmDriver::Init: (BFC FPM Driver) Pool index: 3 pool dimension: 256
[00:00:00 01/01/1970] [tStartup] BcmBfcFpmDriver::Init: (BFC FPM Driver) Lookup desk index: 0 pool dimension: 3
...
[00:00:18 01/01/1970] [Scan Downstream Thread] BcmGenericCmDownstreamScanThread::ThreadMain: (Scan Downstream Thread) Scanning for a Downstream Channel...
[00:00:18 01/01/1970] [Scan Downstream Thread] BcmGenericCmDownstreamScanThread::ScanStarting: (Scan Downstream Thread) Scanning STD & HRC Annex B channel plan frequencies
Resetting EnergyDetected to false.
Forgetting vitality frequency.
Executing quick scan algorithm...
Sort 'assist' or '?' for a listing of instructions...
CM> Scanned 489000000 Hz...
Scanned 495000000 Hz...
Scanned 501000000 Hz...
Scanned 507000000 Hz...
Scanned 513000000 Hz...
eCos Console
The eCos console comprises many configuration and debugging instructions. The
course of flodding the output will be stopped with the next instructions:
As a aspect be aware, these “directories” should not an actual filesystem, they’re only a
manner of organizing teams of instructions.
Telnet entry additionally works on the IP tackle 192.168.100.1
. The username is
“technician” and the password is what I modified it to earlier when modifying
the configuration. Telnet places you right into a restricted model of the shell, however
the total shell will be accessed with the su
command and the password brcm
when prompted (credit score to jclehner, writer of bcm2-utils for that one). This
password could fluctuate relying on the machine producer.
Initially, I simply wished to see if I may allow the web-based spectrum
analyzer which appears to be disabled on this machine. There have been some settings
associated to it, however nothing about enabling or disabling the exterior interface.
After spending a number of hours poking round within the varied menus and instructions
I made a decision it was time to maneuver on to analyzing OS.
Reverse engineering the firmware
Due to the boot log, we all know the OS is situated at 0x20000 and is LZMA
compressed.
The README of bcm2-utils
pointed me to a
Broadcom repo
containing a utility referred to as ProgramStore
for extracting the OS picture.
After constructing ProgramStore
I used to be in a position to extract the decompressed picture with
the next command:
./ProgramStore -f ./ecram_sto.bin -o decompressed_fw.bin -c 4 -x
Now we will throw it into Ghidra utilizing the bottom
tackle from the boot log, 0x80004000 and setting the structure to
huge endian MIPS.
After working the auto evaluation it’s time to start out digging round. Happily,
there numerous debug strings that makes some features very simple to determine.
I began naming any features I got here throughout utilizing the strings in addition to
the operate signature together with the context it’s used and any
cross references. Finally, when ending up in an unknown operate, the cross
references to named features give some clue as to the context of the operate
finally making issues slightly simpler.
For essentially the most half, I used to be simply looking out fascinating strings resembling “tuner” and
“ADC” and attempting to know as a lot as I may, going wherever the varied
operate calls took me and maintaining notes on fascinating features, constructions,
addresses, and theories for a way issues labored in a separate textual content doc.
One helpful function of the eCos console is the name
command which permits you
to name a operate at an arbitrary tackle with the the given arguments. This
permits us to truly execute a operate to check a speculation about what it
does.
The working system has a considerable amount of Broadcom code on high of it which is
all written in C++. This makes reversing it considerably extra annoying by
including numerous indirection relating to operate calls and polymorphism.
For instance you’ll typically see code like this:
case 0x24:
uVar23 = (**(code **)(*piParm1_00 + 0x1c))(piParm1_00);
Not solely does this make the code very exhausting to observe, it additionally means Ghidra
can not observe cross references to features which are referred to as on this manner.
Generally the category will be decided by following the article pointer all of the
manner again to the place it’s initialized, however different instances it’s simpler to only patch
a operate the place the unknown object is used to write down the vale of the pointer
to a recognized location after which return. This may be carried out with the write_memory
command within the eCos console. Then the name
command can be utilized to name the
operate containing the unknown object after which read_memory
can be utilized to
retrieve the pointer from the recognized location. These sort of hacks in addition to
combining static and dynamic evaluation are essential to keep away from getting caught.
Some features would trigger the machine to crash after I tried to name them.
Nearer inspection reveals they’re taking greater than 4 arguments and utilizing the
t0, t1, t2, and t3 registers for the extra arguments. It is a little
uncommon for a 32-bit MIPS machine, however because it seems the calling conference
is decided by the ABI and MIPS has numerous completely different ABIs. It’s
suprisingly exhausting to seek out details about the precise calling conventions, however
utilizing Godbolt, I used to be in a position to decide that EABI is
the most probably candidate which makes use of t0-t3 for extra arguments.
Sadly, Ghidra doesn’t appear to help MIPS EABI, however manually setting
the argument registers on a couple of features will not be too inconvenient and the
arguments appear to be the one consequential distinction.
After digging round for some time, I set my sights on the spectrum analyzer. I
was by no means in a position to determine tips on how to allow the net interface, though the
code for it was there.
I discovered many different helpful features resembling these used to set the frequency
of a downstream (recieving) channel, the socket/bind/pay attention/ship/recv
features, thread creation features, and features for studying and writing
to registers of tuner and LNA (low noise amplifier).
Breakthrough
Finally I discovered a console command to carry out a bandpower measurement for a
given frequency vary.
I started intently following the execution to see what it does
with the frequency vary arguments and located that it calls a really
acquainted operate – virtually an identical to the one used to tune downstream
channels, however the reminiscence mapped register addresses the place the frequency is ready
are simply above these used for regular channels. This means that the spectrum
analyzer makes use of an additional channel that in any other case operates in a really comparable manner
to the traditional downstream channels almost about tuning and setting the acquire.
Persevering with on, the measurement operate writes the bodily tackle of a
vacation spot buffer to a reminiscence mapped register after which units a bit in one other
register and loops till it’s unset once more. Then it calls the operate that
presumably computes an FFT, passing in buffer tackle. After the computation,
one other operate does some processing on the buffer, however leaves it in any other case
intact.
After sticking a jumper wire into the coaxial connector to behave as as an
antenna, I referred to as the bandpower operate after which did a read_memory
on the
vacation spot buffer.
CM> read_memory -n256 0x86fb3e80
86fb3e80: 00 00 06 8c 00 3f fe 48 00 00 06 41 00 20 00 3d | .....?.H...A. .=
86fb3e90: 00 00 08 56 00 20 02 11 00 00 0a b3 00 20 03 f2 | ...V. ....... ..
86fb3ea0: 00 00 0a 50 00 20 04 84 00 00 06 61 00 20 03 d7 | ...P. .....a. ..
86fb3eb0: 00 00 01 1d 00 20 02 da 00 1f fd f4 00 20 00 4d | ..... ....... .M
86fb3ec0: 00 1f fd 11 00 3f fc 20 00 1f fb 95 00 3f fa advert | .....?. .....?..
86fb3ed0: 00 1f fa 32 00 3f fd fc 00 1f fc a3 00 20 00 cb | ...2.?....... ..
86fb3ee0: 00 00 01 97 00 3f fe b5 00 00 04 0f 00 3f fb 6a | .....?.......?.j
86fb3ef0: 00 00 03 9f 00 3f fb d6 00 00 03 1d 00 3f fe 55 | .....?.......?.U
86fb3f00: 00 00 02 f8 00 3f ff a9 00 00 02 ee 00 20 01 49 | .....?....... .I
86fb3f10: 00 00 03 8f 00 20 04 87 00 00 03 94 00 20 05 09 | ..... ....... ..
86fb3f20: 00 00 01 81 00 3f ff bb 00 1f ff 14 00 3f fa 97 | .....?.......?..
86fb3f30: 00 1f fe 8d 00 3f fc 9d 00 1f ff 89 00 20 01 82 | .....?....... ..
86fb3f40: 00 00 00 be 00 20 00 09 00 00 01 8f 00 3f fa 3a | ..... .......?.:
86fb3f50: 00 00 01 78 00 3f fa 66 00 00 00 7b 00 20 01 35 | ...x.?.f...{. .5
86fb3f60: 00 1f ff 79 00 20 04 f6 00 1f fe e2 00 20 02 62 | ...y. ....... .b
86fb3f70: 00 1f fd 93 00 3f ff 4d 00 1f fa ee 00 3f fe 16 | .....?.M.....?..
My hope was that this was I/Q information and
this actually regarded promising. This was supported by the operate that
processes the information after the FFT – it checks if the 0x00200000 bit is zero on
the primary 32-bit phrase, and in that case drops the primary and final phrase of information.
I hypothesized that this bit signifies if the pattern is an I or a Q worth, and
if the primary pattern is a Q, it drops the unrivaled Q from
the start and unmatched I from the top. For instance:
Case 1: Case 2:
Q IQ IQ IQ I IQ IQ IQ IQ
| | do nothing
v v
IQ IQ IQ IQ IQ IQ IQ
The one strategy to know for certain was to seize some extra information and analyze it.
Whats up World
To make issues simpler going ahead, I made a decision to write down a program that might
run on the modem to name the tune and bandpower features, after which open a
listening sock and ship the contents of the buffer again over a TCP connection.
I ought to additionally be aware I ended engaged on the Raspberry Pi at this level so
that I’d not must create an ARM-to-MIPS crosscompiler.
The essential method to write down code that may be loaded and executed on
“almost-bare steel” is as follows.
By including the signatures of the exterior features we want to use to a header
file and utilizing a linker script containing the addresses of the features, it
is straightforward to compile a program that makes use of these features. Moreover, to
make this system work when loaded at a predetermined reminiscence location and
make sure the entry level is at that tackle, a piece map is be used.
The linker script appears to be like one thing like this:
memset = 0x80522d7c;
memcpy = 0x80004f30;
malloc = 0x80596998;
printf = 0x8052b178;
socket = 0x80332fd0;
bind = 0x800ae7bc;
pay attention = 0x80412ed4;
settle for = 0x80413118;
ship = 0x80413240;
recv = 0x804134bc;
tune_aux_channel = 0x80082108;
SECTIONS
{
. = 0x80810000;
.begin : { *(.begin) }
.textual content : { *(.textual content) }
.information : { *(.information) }
.rodata : { *(.rodata) }
}
It’s constructed with the next command:
mips-linux-gcc measure.c
-march=mips32
-mabi=eabi
-msoft-float
-mno-abicalls
-fno-builtin
-nostdlib
-nodefaultlibs
-nostartfiles
-T ./script.ld
The MIPS CPU doesn’t have an FPU so -msoft-float
is used. -mno-abicalls
appears to be required when utilizing -mabi=eabi
. -fno-builtin
prevents the
compiler from optimizing sure sections by including calls to features like
memcpy
which might end in an undefined image. -nostdlib
and
-nostartfiles
stop the compiler from utilizing the usual c library the
“crt0.o” entrypoint which does some setup we don’t care about.
Utilizing objcopy
we will extract simply the sections we care about out of the
compiled elf.
mips-linux-objcopy -O binary
-j .begin
-j .textual content
-j .information
-j .rodata
a.out bin
And eventually, to truly load it I wrote a Python script that makes use of pexpect to
telnet into the modem and write the binary to the goal tackle utilizing the
write_memory
command. This system is executed with the name
command.
To see if I may decide up FM radio broadcasts, I tuned it to 100MHz and
grabbed some information.
Utilizing the numpy, scipy, and matplotlib Python libraries, I used to be in a position to
interpret the information a fancy valued samples, compute an FFT and plot it to
see a pleasant band cross filtered spectrum with distinct spikes.
![]() |
---|
The plotted frequency spectrum |
On the time I used to be nonetheless not likely satisfied, however on reflection this actually
does display that I had efficiently captured advanced samples as such a
plot wouldn’t be potential with the my script had been that not the case.
I used to be not going to be absolutely satisfied till I may demodulate the sign and
hearken to the FM radio broadcasts. Shifting the spectrum to middle one of many
spikes, decimating it to isolate the frequency vary, and utilizing a quite simple
demodulation method I discovered on-line for complex-valued FM, I used to be in a position to
clearly see the completely different elements of the printed together with the (what must be)
19kHz pilot tone.
![]() |
---|
The frequency spectrum of the demodulated sign |
With some experimentation, I decided {that a} pattern price of 15 million samples
per second places the pilot tone proper at 19kHz. This appears in line with the
indisputable fact that the bandpower operate measures in blocks of seven.5MHz. Though you get
a full 15MHz since these are complex-valued samples, the usable vary of the
band cross filter is extra like 7.5-8MHz with an assumed 15MHz pattern price which
matches up.
Optimizations
At 15 million samples per second and every pattern with occupying 8 bytes,
lower than a second of information will be saved within the roughly 100MB of free
RAM.
One apparent enchancment could be to ship the information after filling the buffer and
then seize extra information. The processing time and community throughput meant that
there was about an 11 second hole between captures. This was reduce right down to about 5
seconds by implementing a brand new operate that units up the registers and initiates
the seize, eliminating the FFT calculation and different processing.
After some experimentation with the unknown register values in hopes of discovering
one that might have an effect on the pattern price, I discovered a bit that seems to restrict the
I and Q values to 14 important bits. Whereas they had been nonetheless occupying 8 bytes
per pattern, this meant I may pack two of them right into a single 32-bit phrase.
Observe: I’m not certain the ADC is definitely sampling 14 / 20 bits as that appears
slightly excessive, however that’s the efficient dimension of every pattern worth.
I wrote one other operate to course of every seize, decide if it begins
with an I or a Q worth after which iterate via the buffer packing every
I/Q pair right into a single integer and writing it to the subsequent place within the
buffer. This alone didn’t enhance the efficiency a lot, however by solely taking
each Nth pattern, I may decrease the efficient pattern price, shorten the
processing time, and cut back the variety of bytes I needed to ship again which
significantly improved the latency.
One other enchancment was threading. I discovered the features used to create and
begin new threads in addition to these used for counting semaphores on the OS
due to some debug messages. With these instruments, I may have two or extra
buffers. Then one thread constantly captures information into the subsequent obtainable
buffer after which indicators one other thread with a semaphore that it’s carried out
writing. The second thread packs and downsamples the information, sends it over the
community, after which indicators that the buffer is on the market to be written to once more.
With these enhancements and the pattern price diminished by an element of 32 (right down to
464kHz), I may endlessly seize and ship information whereas dropping about 12%. I
consider that is largely due to a couple milliseconds of inherent latency between
when the seize is completed and the “carried out” flag is ready. Moreover, I do
not know if there’s a strategy to generate an interrupt or one thing when it
finishes so I’ve to restort to sleeping shortly loop since that’s the way it
is finished within the authentic operate.
My hope is that there’s some kind of clock divider register to scale back the
clock of the ADC to decrease the pattern price which can cut back this latancy and
remove postprocessing time, however I’ve not but discovered one.
Downsampling this manner appears to lose numerous the data – previous about 16
instances the noise flooring is loads greater and the stereo channels of FM radio
broadcasts are not discernable.
Though dropping some information is dangerous some some functions, it may stream FM
radio pretty seamlessly, though the audio needs to be slowed down barely
so it’s not consumed quicker than it’s being recieved. I’ve additionally been in a position
to be recieved. I’ve additionally been in a position to decide up the 154MHz narrowband FM radio
utilized by the native fireplace division.
Here is a short sample of some demodulated audio captured with the modem
Conclusion
Though this undertaking was largely only a problem to myself and isn’t meant
as a severe SDR, I’m glad with the outcomes and hope to proceed to
enhance it.
This quote actually resonated with me, so as soon as once more within the phrases of VK4HAT, “With so few firsts obtainable in life, take people who current themselves and have a crack”